diff --git a/.github/workflows/benchmark-weights.yml b/.github/workflows/benchmark-weights.yml new file mode 100644 index 000000000..e69de29bb diff --git a/.github/workflows/check-devnet.yml b/.github/workflows/check-devnet.yml index fcc9809d3..3d7f17723 100644 --- a/.github/workflows/check-devnet.yml +++ b/.github/workflows/check-devnet.yml @@ -2,7 +2,7 @@ name: Devnet Deploy Check on: pull_request: - branches: [devnet] + branches: [devnet, devnet-ready] env: CARGO_TERM_COLOR: always @@ -11,6 +11,7 @@ jobs: check-spec-version: name: Check spec_version bump runs-on: SubtensorCI + if: ${{ !contains(github.event.pull_request.labels.*.name, 'no-spec-version-bump') }} steps: - name: Dependencies run: | @@ -37,7 +38,7 @@ jobs: echo "network spec_version: $spec_version" if (( $(echo "$local_spec_version <= $spec_version" | bc -l) )); then echo "$local_spec_version ≯ $spec_version ❌"; exit 1; fi echo "$local_spec_version > $spec_version ✅" - + check-devnet-migrations: name: check devnet migrations runs-on: ubuntu-22.04 @@ -51,4 +52,4 @@ jobs: runtime-package: "node-subtensor-runtime" node-uri: "wss://dev.chain.opentensor.ai:443" checks: "pre-and-post" - extra-args: "--disable-spec-version-check --no-weight-warnings" \ No newline at end of file + extra-args: "--disable-spec-version-check --no-weight-warnings" diff --git a/.github/workflows/check-finney.yml b/.github/workflows/check-finney.yml index 4bb12caf2..3e9fb5994 100644 --- a/.github/workflows/check-finney.yml +++ b/.github/workflows/check-finney.yml @@ -11,6 +11,7 @@ jobs: check-spec-version: name: Check spec_version bump runs-on: SubtensorCI + if: ${{ !contains(github.event.pull_request.labels.*.name, 'no-spec-version-bump') }} steps: - name: Dependencies run: | diff --git a/.github/workflows/check-rust.yml b/.github/workflows/check-rust.yml index e95308861..797ad4df4 100644 --- a/.github/workflows/check-rust.yml +++ b/.github/workflows/check-rust.yml @@ -113,6 +113,54 @@ jobs: - name: cargo clippy --workspace --all-targets -- -D warnings run: cargo clippy --workspace --all-targets -- -D warnings + cargo-check-lints: + name: check custom lints + runs-on: SubtensorCI + strategy: + matrix: + rust-branch: + - stable + rust-target: + - x86_64-unknown-linux-gnu + # - x86_64-apple-darwin + os: + - ubuntu-latest + # - macos-latest + env: + RELEASE_NAME: development + RUSTV: ${{ matrix.rust-branch }} + RUSTFLAGS: -D warnings + RUST_BACKTRACE: full + RUST_BIN_DIR: target/${{ matrix.rust-target }} + SKIP_WASM_BUILD: 1 + TARGET: ${{ matrix.rust-target }} + steps: + - name: Check-out repository under $GITHUB_WORKSPACE + uses: actions/checkout@v4 + + - name: Install dependencies + run: | + sudo apt-get update && + sudo apt-get install -y 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 }} + components: rustfmt, clippy + profile: minimal + + - name: Utilize Shared Rust Cache + uses: Swatinem/rust-cache@v2.2.1 + with: + key: ${{ matrix.os }}-${{ env.RUST_BIN_DIR }} + + - name: check lints + run: | + set -o pipefail # Ensure the pipeline fails if any command in the pipeline fails + cargo check 2>&1 | sed -r "s/\x1B\[[0-9;]*[mK]//g" | tee /dev/tty | grep -q "^warning:" && \ + (echo "Build emitted the following warnings:" >&2 && exit 1) || echo "No warnings found." + cargo-clippy-all-features: name: cargo clippy --all-features runs-on: SubtensorCI @@ -338,7 +386,3 @@ jobs: - name: Check features run: zepter run check - - - - diff --git a/.github/workflows/check-testnet.yml b/.github/workflows/check-testnet.yml index 71c46557c..c18b45ac2 100644 --- a/.github/workflows/check-testnet.yml +++ b/.github/workflows/check-testnet.yml @@ -2,7 +2,7 @@ name: Testnet Deploy Check on: pull_request: - branches: [testnet] + branches: [testnet, testnet-ready] env: CARGO_TERM_COLOR: always @@ -11,6 +11,7 @@ jobs: check-spec-version: name: Check spec_version bump runs-on: SubtensorCI + if: ${{ !contains(github.event.pull_request.labels.*.name, 'no-spec-version-bump') }} steps: - name: Dependencies run: | diff --git a/.github/workflows/devnet-labels.yml b/.github/workflows/devnet-labels.yml deleted file mode 100644 index 85d9e7eed..000000000 --- a/.github/workflows/devnet-labels.yml +++ /dev/null @@ -1,19 +0,0 @@ -name: Tested on Devnet -on: - pull_request: - types: [opened, labeled, unlabeled, synchronize] - branches: [main] -jobs: - check-labels: - runs-on: ubuntu-latest - permissions: - issues: write - pull-requests: write - steps: - - uses: mheap/github-action-required-labels@v5 - with: - mode: minimum - count: 1 - labels: | - devnet-pass - devnet-skip diff --git a/.github/workflows/devnet-ready-labels.yml b/.github/workflows/devnet-ready-labels.yml deleted file mode 100644 index ab53327e7..000000000 --- a/.github/workflows/devnet-ready-labels.yml +++ /dev/null @@ -1,17 +0,0 @@ -name: devnet-companion Label Check -on: - pull_request: - types: [opened, labeled, unlabeled, synchronize] - branches: [devnet-ready] -jobs: - check-labels: - runs-on: ubuntu-latest - permissions: - issues: write - pull-requests: write - steps: - - uses: mheap/github-action-required-labels@v5 - with: - mode: minimum - count: 1 - labels: devnet-companion diff --git a/.github/workflows/testnet-labels.yml b/.github/workflows/testnet-labels.yml deleted file mode 100644 index b4aabd958..000000000 --- a/.github/workflows/testnet-labels.yml +++ /dev/null @@ -1,19 +0,0 @@ -name: Tested on Testnet -on: - pull_request: - types: [opened, labeled, unlabeled, synchronize] - branches: [main] -jobs: - check-labels: - runs-on: ubuntu-latest - permissions: - issues: write - pull-requests: write - steps: - - uses: mheap/github-action-required-labels@v5 - with: - mode: minimum - count: 1 - labels: | - testnet-pass - testnet-skip diff --git a/.github/workflows/testnet-ready-labels.yml b/.github/workflows/testnet-ready-labels.yml deleted file mode 100644 index 8570d2011..000000000 --- a/.github/workflows/testnet-ready-labels.yml +++ /dev/null @@ -1,17 +0,0 @@ -name: testnet-companion Label Check -on: - pull_request: - types: [opened, labeled, unlabeled, synchronize] - branches: [testnet-ready] -jobs: - check-labels: - runs-on: ubuntu-latest - permissions: - issues: write - pull-requests: write - steps: - - uses: mheap/github-action-required-labels@v5 - with: - mode: minimum - count: 1 - labels: testnet-companion diff --git a/.rustfmt.toml b/.rustfmt.toml index 24876acd9..9fd9af831 100644 --- a/.rustfmt.toml +++ b/.rustfmt.toml @@ -6,7 +6,7 @@ ## # rustup run nightly -- rustfmt node/src/main.rs -# max_width = 100 +# max_width = 180 # hard_tabs = false # tab_spaces = 4 # newline_style = "Auto" diff --git a/CODEOWNERS b/CODEOWNERS index 5fefbd608..ffc01511b 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -1 +1 @@ -* @sacha-l @lisa-parity +* @unconst diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 132d360b8..8ac806367 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -3,7 +3,7 @@ ## Lifecycle of a Pull Request 1. Individuals wishing to contribute to subtensor should develop their change/feature/fix in a - [Pull Request](https://github.com/opentensor/subtensor/compare) (PR) targeting the `main` + [Pull Request](https://github.com/opentensor/subtensor/compare) (PR) targeting the `devnet-ready` branch of the subtensor GitHub repository. It is recommended to start your pull request as a draft initially until you are ready to have other developers actively look at it. Any changes to pallet/runtime code should be accompanied by integration and/or unit tests fully @@ -13,69 +13,29 @@ Review" and request review from "Nucleus". 3. Core Nucleus team members will review your PR, possibly requesting changes, and will also add appropriate labels to your PR as shown below. Three positive reviews are required. -4. Once the required passing reviews have been obtained, you are ready to request that your PR - be included in the next `devnet` deploy. To do this, you should open a companion PR merging - a copy of your branch into the `devnet-ready` branch. You must include a link to the parent - PR in the description and preface your PR title with "(Devnet Ready)" or the PR will be - closed/ignored. Your companion PR should have the `devnet-companion` label. -5. A core team administrator will review your "(Devnet Ready)" PR, verifying that it logically - matches the changes introduced in the parent PR (there will sometimes be minor differences - due to merge conflicts) and will either request changes or approve the PR and merge it. Once - your companion PR is merged, the administrator will add the `devnet-ready` label to the - parent PR, indicating that the PR is on the `devnet-ready` branch and will be included in - the next deploy to `devnet`. -6. At some point, a core team administrator will open a PR merging the current `devnet-ready` +4. Once the required passing reviews have been obtained, you or an administrator may merge the + PR into the `devnet-ready` branch. +5. At some point, a core team administrator will open a PR merging the current `devnet-ready` branch into `devnet`, and the CI will enforce some additional safety checks on this PR including a requirement that the new `spec_version` be greater than the current on-chain `spec_version`. The PR should include a bulleted list of all PRs included in the deploy so - they can be easily found after the fact (TODO: automate this). This PR will require two - reviews from the core team as a sanity check. After merging, the administrator will then - need to update all PRs with the `devnet-ready` label to instead have the `on-devnet` label - (TODO: automate this upon merge). The administrator will then deploy `devnet`. -7. Once the `on-devnet` label appears on your PR, if you are a core team member it is your - responsibility to verify that the features/changes/fixes introduced by your PR are - functioning properly on `devnet` by interacting with the live network. If you are an - external contributor, a core team member will be assigned to test this for you. -8. If your feature/change/fix is confirmed working on `devnet`, the `devnet-pass` label should - be added. Otherwise if there are issues, the `devnet-fail` label should be added and you - will need to make changes to your PR and repeat the previous steps in this process. In some - cases a revert PR will need to be created reverting your changes from the `pre-devnet` and - `devnet` branches, respectively. -9. Once `devnet-pass` has been added to your PR, it is eligible for inclusion in the next - `testnet` deploy. We typically run `testnet` deploys every other wednesday. -10. On the appropriate date, an administrator will open a PR merging the current `devnet` + they can be easily found after the fact. +6. Once your feature/change/fix is on `devnet`, it is your responsibility to confirm it is + working properly. If it is not working and additional changes are needed, please coordinate + with a core team administrator and/or open up a new PR into `devnet` either reverting your + changes or making any required changes in order for the feature to function properly. +7. On the appropriate date, an administrator will open a PR merging the current `devnet` branch into `testnet`. This PR should include a bulleted list of all PRs included in the - deploy so they can be easily found after the fact (TODO: automate this). The PR should - exclude any PRs that currently have the `devnet-fail` label via a revert (TODO: enforce via - CI). This PR will require two reviews from the core team as a sanity check. After merging - into `testnet`, the administrator will then need to run the deploy and update all PRs - included in the deploy with the `on-testnet` label (TODO: automate this upon merge). Next - the administrator must cut a (pre-release) release in GitHub for `testnet` (TODO: github - action to generate the release and release notes). -11. Once the `on-testnet` label appears on your PR, if you are a core team member it is your - responsibility to once again verify that the features/changes/fixes introduced by your PR - are functioning properly on `testnet` by interacting with the live network, if applicable. - If you are an external contributor, a core team member may be assigned to do this testing - for you but otherwise it will be your responsibility to show evidence on the PR that the - testing is successful. Once this has been verified, the `testnet-pass` label should be - added. If testing fails, the `testnet-fail` label should be added and PRs should be opened - reverting the change from `devnet-ready`, and then a PR should be opened merging the - modified `devnet` into `testnet`. These revert PRs, if they occur, _must_ be merged before - a new deploy can be run (TODO: enforce this via CI). -12. After the SOP period (1 week on `testnet`) has passed and the `testnet-pass` label has been - added, the CI checks on your PR should now turn all green and a core team member will be - able to merge your PR into `main`. At this point your PR is done and is eligible to be - included in the next `finney` deploy (TODO: track and enforce SOP compliance on a per-PR - basis in CI based on the timestamps of label changes). We typically run `finney` deploys - every other Wednesday, so this will typically happen the Wednesday following the Wednesday - your PR was deployed to `testnet`. An administrator will run this deploy. The process the - administrator follows is to open a PR merging `main` into the `finney` branch, which will - always track the current state of `finney`. This PR automatically has some additional - checks on it such as asserting that the spec_version gets bumped properly and other sanity - checks designed to stop a bad deploy. Once the PR is reviewed and merged, the administrator - will run the actual deploy. Once that is successful, the administrator will cut a new - GitHub release tagged off of the latest `main` branch commit that was included in the - deploy, and announcements will be made regarding the release. + deploy so they can be easily found after the fact (TODO: automate this). This PR is merged, + the administrator will deploy `testnet` and cut a (pre-release) release in GitHub for + `testnet` (TODO: github action to generate the release and release notes). +11. It is now your responsibility to once again check that your feature/change/fix is working + properly, this time on `testnet`. Once again if it is not working or additional changes are + needed, please coordinate with a core team administrator ASAP and/or open up a new PR into + `testnet` either reverting your changes or making any required changes in order for the + feature to function properly. +12. At some point the administrator will merge current `testnet` into `main` and cut a new + deploy to mainnet/finney. ## PR Labels @@ -85,40 +45,24 @@ | `blue-team` | PR is focused on preventative/safety measures and/or dev UX improvements | none | | `runtime` | PR contains substantive changes to runtime / pallet code | none | | `breaking-change` | PR requires synchronized changes with bittensor | Triggers an automatic bot message so the relevant teams are made aware of the change well in advance | -| `migration` | PR contains one or more migrations | none | -| `devnet-companion` | Designates a devnet companion PR | Presence of `devnet-companion` label is checked | -| `devnet-ready` | PR's branch has been merged into the `devnet-ready` branch and will be included in the next `devnet` deploy | none | -| `on-devnet` | PR has been deployed to `devnet` | Removes `devnet-ready` | -| `devnet-pass` | PR has passed manual testing on `devnet` | `devnet-pass` or `devnet-skip` required | -| `devnet-skip` | Allows a critical hotfix PR to skip required testing on `devnet` | `devnet-pass` or `devnet-skip` required | -| `devnet-fail` | PR has failed manual testing on `devnet` and requires modification | none | -| `testnet-companion` | Designates a testnet companion PR | Presence of `testnet-companion` label is checked | -| `on-testnet` | PR has been deployed to `testnet` | none | -| `testnet-pass` | PR has passed manual testing on `testnet` | `testnet-pass` or `testnet-skip` required | -| `testnet-skip` | Allows a critical hotfix PR to skip required manual testing and SOP on `testnet` | `testnet-pass` or `testnet-skip` required | -| `testnet-fail` | PR has failed manual testing on `testnet` and requires modification | none | - ## Branches ### `devnet-ready` -Companion PRs merge into this branch, eventually accumulating into a merge of `devnet-ready` -into `devnet`, coinciding with a deploy of `devnet`. +All new feature/change/fix PRs should merge into this branch. #### Restrictions * no deleting the branch * no force pushes * no direct pushes -* require 1 positive review from an administrator -* new code changes invalidate existing reviews +* require 3 positive review from an administrator +* new code changes do _not_ invalidate existing reviews * only merge commit style merging allowed #### CI-Enforced Restrictions * `check-rust.yml` must pass -* TODO: parent PR must be linked to in description -* TODO: parent PR must have the required number of positive reviews ### `devnet` @@ -164,33 +108,8 @@ tags for `testnet` releases. ### `main` -Default branch for all new PRs. Slightly ahead of what is currently on `finney`. When a PR is all -green and "done", meaning it has been tested on `devnet` and `testnet`, it can be merged into -`main`. Contains tags for `finney` releases. - -#### Restrictions -* no deleting the branch -* no force pushes -* no direct pushes -* require 3 positive reviews from core team members -* new code changes invalidate existing reviews -* all conversations must be resolved -* only merge commit style merging allowed - -#### CI-Enforced Restrictions -* `check-rust.yml` must pass -* `check-labels.yml` must pass -* must have `devnet-skip` or `devnet-pass` label -* must have `testnet-skip` or `testnet-pass` label -* if `breaking-change` label is present, bot will message the appropriate teams -* TODO: when we get auditing, presence of `needs-audit` label = require a review from auditor -* TODO: track SOP on PR based on label age - - -### `finney` - Tracks the current state of what is deployed to `finney` (mainnet). Updated via an -administrator-submitted PR merging `main` into `finney` in concert with a `finney` deploy. +administrator-submitted PR merging `testnet` into `main` in concert with a `finney` deploy. #### Restrictions * no deleting the branch @@ -203,5 +122,5 @@ administrator-submitted PR merging `main` into `finney` in concert with a `finne #### CI-Enforced Restrictions * `check-rust.yml` must pass * `check-finney.yml` must pass -* spec_version must be greater than what is currently on live `finney` +* `spec_version` must be greater than what is currently on live `finney` * TODO: other pre-deploy sanity checks here diff --git a/Cargo.lock b/Cargo.lock index 5e46fa1a0..698607e7c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -223,7 +223,7 @@ dependencies = [ "proc-macro-error", "proc-macro2", "quote", - "syn 2.0.67", + "syn 2.0.71", ] [[package]] @@ -625,7 +625,7 @@ checksum = "c6fa2087f2753a7da8cc1c0dbfcf89579dd57458e36769de5ac750b4671737ca" dependencies = [ "proc-macro2", "quote", - "syn 2.0.67", + "syn 2.0.71", ] [[package]] @@ -751,7 +751,7 @@ dependencies = [ "regex", "rustc-hash", "shlex", - "syn 2.0.67", + "syn 2.0.71", ] [[package]] @@ -1159,7 +1159,7 @@ dependencies = [ "heck 0.5.0", "proc-macro2", "quote", - "syn 2.0.67", + "syn 2.0.71", ] [[package]] @@ -1198,7 +1198,7 @@ dependencies = [ [[package]] name = "common" version = "0.1.0" -source = "git+https://github.com/w3f/ring-proof#b273d33f9981e2bb3375ab45faeb537f7ee35224" +source = "git+https://github.com/w3f/ring-proof#665f5f51af5734c7b6d90b985dd6861d4c5b4752" dependencies = [ "ark-ec", "ark-ff", @@ -1553,7 +1553,7 @@ checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3" dependencies = [ "proc-macro2", "quote", - "syn 2.0.67", + "syn 2.0.71", ] [[package]] @@ -1580,7 +1580,7 @@ dependencies = [ "proc-macro2", "quote", "scratch", - "syn 2.0.67", + "syn 2.0.71", ] [[package]] @@ -1597,7 +1597,7 @@ checksum = "4b2c1c1776b986979be68bb2285da855f8d8a35851a769fca8740df7c3d07877" dependencies = [ "proc-macro2", "quote", - "syn 2.0.67", + "syn 2.0.71", ] [[package]] @@ -1621,7 +1621,7 @@ dependencies = [ "proc-macro2", "quote", "strsim", - "syn 2.0.67", + "syn 2.0.71", ] [[package]] @@ -1632,7 +1632,7 @@ checksum = "733cabb43482b1a1b53eee8583c2b9e8684d592215ea83efd305dd31bc2f0178" dependencies = [ "darling_core", "quote", - "syn 2.0.67", + "syn 2.0.71", ] [[package]] @@ -1738,7 +1738,7 @@ checksum = "d65d7ce8132b7c0e54497a4d9a55a1c2a0912a0d786cf894472ba818fba45762" dependencies = [ "proc-macro2", "quote", - "syn 2.0.67", + "syn 2.0.71", ] [[package]] @@ -1751,7 +1751,7 @@ dependencies = [ "proc-macro2", "quote", "rustc_version 0.4.0", - "syn 2.0.67", + "syn 2.0.71", ] [[package]] @@ -1840,7 +1840,7 @@ checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.67", + "syn 2.0.71", ] [[package]] @@ -1880,7 +1880,7 @@ dependencies = [ "proc-macro2", "quote", "regex", - "syn 2.0.67", + "syn 2.0.71", "termcolor", "toml 0.8.14", "walkdir", @@ -2040,7 +2040,7 @@ checksum = "de0d48a183585823424a4ce1aa132d174a6a81bd540895822eb4c8373a8e49e8" dependencies = [ "proc-macro2", "quote", - "syn 2.0.67", + "syn 2.0.71", ] [[package]] @@ -2153,7 +2153,7 @@ dependencies = [ "prettyplease 0.2.20", "proc-macro2", "quote", - "syn 2.0.67", + "syn 2.0.71", ] [[package]] @@ -2504,7 +2504,7 @@ dependencies = [ "proc-macro2", "quote", "sp-crypto-hashing", - "syn 2.0.67", + "syn 2.0.71", ] [[package]] @@ -2516,7 +2516,7 @@ dependencies = [ "proc-macro-crate 3.1.0", "proc-macro2", "quote", - "syn 2.0.67", + "syn 2.0.71", ] [[package]] @@ -2526,7 +2526,7 @@ source = "git+https://github.com/paritytech/polkadot-sdk.git?tag=v1.10.0-rc3#8d2 dependencies = [ "proc-macro2", "quote", - "syn 2.0.67", + "syn 2.0.71", ] [[package]] @@ -2677,7 +2677,7 @@ checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" dependencies = [ "proc-macro2", "quote", - "syn 2.0.67", + "syn 2.0.71", ] [[package]] @@ -3468,7 +3468,7 @@ dependencies = [ "proc-macro-crate 3.1.0", "proc-macro2", "quote", - "syn 2.0.67", + "syn 2.0.71", ] [[package]] @@ -4150,9 +4150,9 @@ dependencies = [ [[package]] name = "log" -version = "0.4.21" +version = "0.4.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c" +checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" [[package]] name = "lru" @@ -4219,7 +4219,7 @@ dependencies = [ "macro_magic_core", "macro_magic_macros", "quote", - "syn 2.0.67", + "syn 2.0.71", ] [[package]] @@ -4233,7 +4233,7 @@ dependencies = [ "macro_magic_core_macros", "proc-macro2", "quote", - "syn 2.0.67", + "syn 2.0.71", ] [[package]] @@ -4244,7 +4244,7 @@ checksum = "9ea73aa640dc01d62a590d48c0c3521ed739d53b27f919b25c3551e233481654" dependencies = [ "proc-macro2", "quote", - "syn 2.0.67", + "syn 2.0.71", ] [[package]] @@ -4255,7 +4255,7 @@ checksum = "ef9d79ae96aaba821963320eb2b6e34d17df1e5a83d8a1985c29cc5be59577b3" dependencies = [ "macro_magic_core", "quote", - "syn 2.0.67", + "syn 2.0.71", ] [[package]] @@ -4976,6 +4976,7 @@ dependencies = [ "frame-system", "log", "pallet-balances", + "pallet-scheduler", "pallet-subtensor", "parity-scale-codec", "scale-info", @@ -4983,6 +4984,7 @@ dependencies = [ "sp-core", "sp-io", "sp-runtime", + "sp-std 14.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?tag=v1.10.0-rc3)", "sp-tracing 16.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?tag=v1.10.0-rc3)", "sp-weights", "substrate-fixed", @@ -5264,6 +5266,8 @@ dependencies = [ "pallet-balances", "pallet-collective", "pallet-membership", + "pallet-preimage", + "pallet-scheduler", "pallet-transaction-payment", "pallet-utility", "parity-scale-codec", @@ -5273,6 +5277,7 @@ dependencies = [ "serde", "serde-tuple-vec-map", "serde_bytes", + "serde_json", "serde_with", "sp-core", "sp-io", @@ -5621,7 +5626,7 @@ dependencies = [ "pest_meta", "proc-macro2", "quote", - "syn 2.0.67", + "syn 2.0.71", ] [[package]] @@ -5662,7 +5667,7 @@ checksum = "2f38a4412a78282e09a2cf38d195ea5420d15ba0602cb375210efbc877243965" dependencies = [ "proc-macro2", "quote", - "syn 2.0.67", + "syn 2.0.71", ] [[package]] @@ -5748,7 +5753,7 @@ dependencies = [ "polkavm-common", "proc-macro2", "quote", - "syn 2.0.67", + "syn 2.0.71", ] [[package]] @@ -5758,7 +5763,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8ba81f7b5faac81e528eb6158a6f3c9e0bb1008e0ffa19653bc8dea925ecb429" dependencies = [ "polkavm-derive-impl", - "syn 2.0.67", + "syn 2.0.71", ] [[package]] @@ -5885,7 +5890,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5f12335488a2f3b0a83b14edad48dca9879ce89b2edd10e80237e4e852dd645e" dependencies = [ "proc-macro2", - "syn 2.0.67", + "syn 2.0.71", ] [[package]] @@ -5953,7 +5958,7 @@ checksum = "834da187cfe638ae8abb0203f0b33e5ccdb02a28e7199f2f47b3e2754f50edca" dependencies = [ "proc-macro2", "quote", - "syn 2.0.67", + "syn 2.0.71", ] [[package]] @@ -5999,7 +6004,7 @@ checksum = "440f724eba9f6996b75d63681b0a92b06947f1457076d503a4d2e2c8f56442b8" dependencies = [ "proc-macro2", "quote", - "syn 2.0.67", + "syn 2.0.71", ] [[package]] @@ -6067,7 +6072,7 @@ dependencies = [ "itertools 0.12.1", "proc-macro2", "quote", - "syn 2.0.67", + "syn 2.0.71", ] [[package]] @@ -6335,7 +6340,7 @@ checksum = "bcc303e793d3734489387d205e9b186fac9c6cfacedd98cbb2e8a5943595f3e6" dependencies = [ "proc-macro2", "quote", - "syn 2.0.67", + "syn 2.0.71", ] [[package]] @@ -6430,13 +6435,14 @@ dependencies = [ [[package]] name = "ring" version = "0.1.0" -source = "git+https://github.com/w3f/ring-proof#b273d33f9981e2bb3375ab45faeb537f7ee35224" +source = "git+https://github.com/w3f/ring-proof#665f5f51af5734c7b6d90b985dd6861d4c5b4752" dependencies = [ "ark-ec", "ark-ff", "ark-poly", "ark-serialize", "ark-std", + "arrayvec", "blake2 0.10.6", "common", "fflonk", @@ -6794,7 +6800,7 @@ dependencies = [ "proc-macro-crate 3.1.0", "proc-macro2", "quote", - "syn 2.0.67", + "syn 2.0.71", ] [[package]] @@ -7628,7 +7634,7 @@ dependencies = [ "proc-macro-crate 3.1.0", "proc-macro2", "quote", - "syn 2.0.67", + "syn 2.0.71", ] [[package]] @@ -7905,9 +7911,9 @@ checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" [[package]] name = "serde" -version = "1.0.203" +version = "1.0.204" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7253ab4de971e72fb7be983802300c30b5a7f0c2e56fab8abfc6a214307c0094" +checksum = "bc76f558e0cbb2a839d37354c575f1dc3fdc6546b5be373ba43d95f231bf7c12" dependencies = [ "serde_derive", ] @@ -7932,13 +7938,13 @@ dependencies = [ [[package]] name = "serde_derive" -version = "1.0.203" +version = "1.0.204" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "500cbc0ebeb6f46627f50f3f5811ccf6bf00643be300b4c3eabc0ef55dc5b5ba" +checksum = "e0cd7e117be63d3c3678776753929474f3b04a43a080c744d6b0ae2a8c28e222" dependencies = [ "proc-macro2", "quote", - "syn 2.0.67", + "syn 2.0.71", ] [[package]] @@ -7986,7 +7992,7 @@ dependencies = [ "darling", "proc-macro2", "quote", - "syn 2.0.67", + "syn 2.0.71", ] [[package]] @@ -8219,7 +8225,7 @@ dependencies = [ "proc-macro-crate 3.1.0", "proc-macro2", "quote", - "syn 2.0.67", + "syn 2.0.71", ] [[package]] @@ -8405,7 +8411,7 @@ dependencies = [ [[package]] name = "sp-crypto-ec-utils" version = "0.10.0" -source = "git+https://github.com/paritytech/polkadot-sdk#c4b3c1c6c6e492c4196e06fbba824a58e8119a3b" +source = "git+https://github.com/paritytech/polkadot-sdk#6a5b6e03bfc8d0c6f5f05f3180313902c15aee84" dependencies = [ "ark-bls12-377", "ark-bls12-377-ext", @@ -8442,7 +8448,7 @@ source = "git+https://github.com/paritytech/polkadot-sdk.git?tag=v1.10.0-rc3#8d2 dependencies = [ "quote", "sp-crypto-hashing", - "syn 2.0.67", + "syn 2.0.71", ] [[package]] @@ -8461,17 +8467,17 @@ source = "git+https://github.com/paritytech/polkadot-sdk.git?tag=v1.10.0-rc3#8d2 dependencies = [ "proc-macro2", "quote", - "syn 2.0.67", + "syn 2.0.71", ] [[package]] name = "sp-debug-derive" version = "14.0.0" -source = "git+https://github.com/paritytech/polkadot-sdk#c4b3c1c6c6e492c4196e06fbba824a58e8119a3b" +source = "git+https://github.com/paritytech/polkadot-sdk#6a5b6e03bfc8d0c6f5f05f3180313902c15aee84" dependencies = [ "proc-macro2", "quote", - "syn 2.0.67", + "syn 2.0.71", ] [[package]] @@ -8487,7 +8493,7 @@ dependencies = [ [[package]] name = "sp-externalities" version = "0.25.0" -source = "git+https://github.com/paritytech/polkadot-sdk#c4b3c1c6c6e492c4196e06fbba824a58e8119a3b" +source = "git+https://github.com/paritytech/polkadot-sdk#6a5b6e03bfc8d0c6f5f05f3180313902c15aee84" dependencies = [ "environmental", "parity-scale-codec", @@ -8670,7 +8676,7 @@ dependencies = [ [[package]] name = "sp-runtime-interface" version = "24.0.0" -source = "git+https://github.com/paritytech/polkadot-sdk#c4b3c1c6c6e492c4196e06fbba824a58e8119a3b" +source = "git+https://github.com/paritytech/polkadot-sdk#6a5b6e03bfc8d0c6f5f05f3180313902c15aee84" dependencies = [ "bytes", "impl-trait-for-tuples", @@ -8696,20 +8702,20 @@ dependencies = [ "proc-macro-crate 3.1.0", "proc-macro2", "quote", - "syn 2.0.67", + "syn 2.0.71", ] [[package]] name = "sp-runtime-interface-proc-macro" version = "17.0.0" -source = "git+https://github.com/paritytech/polkadot-sdk#c4b3c1c6c6e492c4196e06fbba824a58e8119a3b" +source = "git+https://github.com/paritytech/polkadot-sdk#6a5b6e03bfc8d0c6f5f05f3180313902c15aee84" dependencies = [ "Inflector", "expander", "proc-macro-crate 3.1.0", "proc-macro2", "quote", - "syn 2.0.67", + "syn 2.0.71", ] [[package]] @@ -8791,7 +8797,7 @@ source = "git+https://github.com/paritytech/polkadot-sdk.git?tag=v1.10.0-rc3#8d2 [[package]] name = "sp-std" version = "14.0.0" -source = "git+https://github.com/paritytech/polkadot-sdk#c4b3c1c6c6e492c4196e06fbba824a58e8119a3b" +source = "git+https://github.com/paritytech/polkadot-sdk#6a5b6e03bfc8d0c6f5f05f3180313902c15aee84" [[package]] name = "sp-storage" @@ -8808,7 +8814,7 @@ dependencies = [ [[package]] name = "sp-storage" version = "19.0.0" -source = "git+https://github.com/paritytech/polkadot-sdk#c4b3c1c6c6e492c4196e06fbba824a58e8119a3b" +source = "git+https://github.com/paritytech/polkadot-sdk#6a5b6e03bfc8d0c6f5f05f3180313902c15aee84" dependencies = [ "impl-serde", "parity-scale-codec", @@ -8843,7 +8849,7 @@ dependencies = [ [[package]] name = "sp-tracing" version = "16.0.0" -source = "git+https://github.com/paritytech/polkadot-sdk#c4b3c1c6c6e492c4196e06fbba824a58e8119a3b" +source = "git+https://github.com/paritytech/polkadot-sdk#6a5b6e03bfc8d0c6f5f05f3180313902c15aee84" dependencies = [ "parity-scale-codec", "tracing", @@ -8922,7 +8928,7 @@ dependencies = [ "parity-scale-codec", "proc-macro2", "quote", - "syn 2.0.67", + "syn 2.0.71", ] [[package]] @@ -8940,7 +8946,7 @@ dependencies = [ [[package]] name = "sp-wasm-interface" version = "20.0.0" -source = "git+https://github.com/paritytech/polkadot-sdk#c4b3c1c6c6e492c4196e06fbba824a58e8119a3b" +source = "git+https://github.com/paritytech/polkadot-sdk#6a5b6e03bfc8d0c6f5f05f3180313902c15aee84" dependencies = [ "impl-trait-for-tuples", "log", @@ -9091,7 +9097,7 @@ dependencies = [ "proc-macro2", "quote", "rustversion", - "syn 2.0.67", + "syn 2.0.71", ] [[package]] @@ -9114,7 +9120,7 @@ source = "git+https://github.com/paritytech/polkadot-sdk.git?tag=v1.10.0-rc3#8d2 [[package]] name = "substrate-fixed" version = "0.5.9" -source = "git+https://github.com/encointer/substrate-fixed.git?tag=v0.5.9#a4fb461aae6205ffc55bed51254a40c52be04e5d" +source = "git+https://github.com/opentensor/substrate-fixed.git?tag=v0.5.9#a4fb461aae6205ffc55bed51254a40c52be04e5d" dependencies = [ "parity-scale-codec", "scale-info", @@ -9181,6 +9187,23 @@ dependencies = [ "wasm-opt", ] +[[package]] +name = "subtensor" +version = "0.1.0" +dependencies = [ + "node-subtensor", + "node-subtensor-runtime", + "pallet-commitments", + "pallet-subtensor", + "proc-macro2", + "quote", + "rayon", + "subtensor-linting", + "subtensor-macros", + "syn 2.0.71", + "walkdir", +] + [[package]] name = "subtensor-custom-rpc" version = "0.0.2" @@ -9206,6 +9229,15 @@ dependencies = [ "sp-api", ] +[[package]] +name = "subtensor-linting" +version = "0.1.0" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.71", +] + [[package]] name = "subtensor-macros" version = "0.1.0" @@ -9213,7 +9245,7 @@ dependencies = [ "ahash 0.8.11", "proc-macro2", "quote", - "syn 2.0.67", + "syn 2.0.71", ] [[package]] @@ -9251,9 +9283,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.67" +version = "2.0.71" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff8655ed1d86f3af4ee3fd3263786bc14245ad17c4c7e85ba7187fb3ae028c90" +checksum = "b146dcf730474b4bcd16c311627b31ede9ab149045db4d6088b3becaea046462" dependencies = [ "proc-macro2", "quote", @@ -9359,7 +9391,7 @@ checksum = "46c3384250002a6d5af4d114f2845d37b57521033f30d5c3f46c4d70e1197533" dependencies = [ "proc-macro2", "quote", - "syn 2.0.67", + "syn 2.0.71", ] [[package]] @@ -9479,7 +9511,7 @@ checksum = "5f5ae998a069d4b5aba8ee9dad856af7d520c3699e6159b185c2acd48155d39a" dependencies = [ "proc-macro2", "quote", - "syn 2.0.67", + "syn 2.0.71", ] [[package]] @@ -9637,7 +9669,7 @@ checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.67", + "syn 2.0.71", ] [[package]] @@ -10042,7 +10074,7 @@ dependencies = [ "once_cell", "proc-macro2", "quote", - "syn 2.0.67", + "syn 2.0.71", "wasm-bindgen-shared", ] @@ -10076,7 +10108,7 @@ checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.67", + "syn 2.0.71", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -10795,7 +10827,7 @@ checksum = "15e934569e47891f7d9411f1a451d947a60e000ab3bd24fbb970f000387d1b3b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.67", + "syn 2.0.71", ] [[package]] @@ -10815,7 +10847,7 @@ checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" dependencies = [ "proc-macro2", "quote", - "syn 2.0.67", + "syn 2.0.71", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index e8d94e157..b8fe8c4d4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,11 +1,41 @@ +[package] +name = "subtensor" +version = "0.1.0" +description = "Implementation of the bittensor blockchain" +authors = ["Substrate DevHub "] +homepage = "https://substrate.io/" +edition = "2021" +license = "Unlicense" +publish = false +repository = "https://github.com/opentensor/subtensor" + +[dependencies] +node-subtensor = { path = "node", version = "4.0.0-dev" } +pallet-commitments = { path = "pallets/commitments", version = "4.0.0-dev" } +pallet-subtensor = { path = "pallets/subtensor", version = "4.0.0-dev" } +node-subtensor-runtime = { path = "runtime", version = "4.0.0-dev" } +subtensor-macros = { path = "support/macros", version = "0.1.0" } + +[build-dependencies] +subtensor-linting = { path = "support/linting", version = "0.1.0" } +syn.workspace = true +quote.workspace = true +proc-macro2.workspace = true +walkdir.workspace = true +rayon = "1.10" + [workspace] members = [ "node", "pallets/commitments", "pallets/subtensor", + "pallets/admin-utils", + "pallets/collective", + "pallets/registry", "runtime", "support/tools", "support/macros", + "support/linting", ] resolver = "2" @@ -37,13 +67,23 @@ serde_json = { version = "1.0.116", default-features = false } serde_with = { version = "=2.0.0", default-features = false } smallvec = "1.13.2" litep2p = { git = "https://github.com/paritytech/litep2p", branch = "master" } +syn = { version = "2", features = [ + "full", + "visit-mut", + "visit", + "extra-traits", + "parsing", +] } +quote = "1" +proc-macro2 = { version = "1", features = ["span-locations"] } +walkdir = "2" subtensor-macros = { path = "support/macros" } frame-benchmarking = { git = "https://github.com/paritytech/polkadot-sdk.git", tag = "v1.10.0-rc3", default-features = false } frame-benchmarking-cli = { git = "https://github.com/paritytech/polkadot-sdk.git", tag = "v1.10.0-rc3" } frame-executive = { git = "https://github.com/paritytech/polkadot-sdk.git", tag = "v1.10.0-rc3", default-features = false } -frame-metadata-hash-extension = { git = "https://github.com/paritytech/polkadot-sdk.git", tag = "v1.10.0-rc3" , default-features = false } +frame-metadata-hash-extension = { git = "https://github.com/paritytech/polkadot-sdk.git", tag = "v1.10.0-rc3", default-features = false } frame-support = { git = "https://github.com/paritytech/polkadot-sdk.git", tag = "v1.10.0-rc3", default-features = false } frame-system = { git = "https://github.com/paritytech/polkadot-sdk.git", tag = "v1.10.0-rc3", default-features = false } frame-system-benchmarking = { git = "https://github.com/paritytech/polkadot-sdk.git", tag = "v1.10.0-rc3", default-features = false } @@ -112,7 +152,7 @@ sp-version = { git = "https://github.com/paritytech/polkadot-sdk.git", tag = "v1 sp-weights = { git = "https://github.com/paritytech/polkadot-sdk.git", tag = "v1.10.0-rc3", default-features = false } substrate-build-script-utils = { git = "https://github.com/paritytech/polkadot-sdk.git", tag = "v1.10.0-rc3" } -substrate-fixed = { git = "https://github.com/encointer/substrate-fixed.git", tag = "v0.5.9" } +substrate-fixed = { git = "https://github.com/opentensor/substrate-fixed.git", tag = "v0.5.9" } substrate-frame-rpc-system = { git = "https://github.com/paritytech/polkadot-sdk.git", tag = "v1.10.0-rc3" } substrate-wasm-builder = { git = "https://github.com/paritytech/polkadot-sdk.git", tag = "v1.10.0-rc3" } frame-metadata = "16" diff --git a/README.md b/README.md index 47639b640..30f36ffab 100644 --- a/README.md +++ b/README.md @@ -51,7 +51,7 @@ Requirements: ## For Subnet Development -If you are developing and testing subnet incentive mechanism, you will need to run a local subtensor node. Follow the detailed step-by-step instructions provided in the document [Running subtensor locally](./docs/running-subtensor-locally.md) to run either a lite node or an archive node. Also see the [**Subtensor Nodes** section in Bittensor Developer Documentation](https://docs.bittensor.com/subtensor-nodes). +If you are developing and testing subnet incentive mechanism, you will need to run a local subtensor node. Follow the detailed step-by-step instructions provided in the [**Subtensor Nodes** section in Bittensor Developer Documentation](https://docs.bittensor.com/subtensor-nodes). ### Lite node vs Archive node diff --git a/build.rs b/build.rs new file mode 100644 index 000000000..10cac0ea7 --- /dev/null +++ b/build.rs @@ -0,0 +1,96 @@ +use rayon::prelude::*; +use std::{ + env, fs, + path::{Path, PathBuf}, + str::FromStr, + sync::mpsc::channel, +}; +use walkdir::WalkDir; + +use subtensor_linting::*; + +fn main() { + // need to list all rust directories here + println!("cargo:rerun-if-changed=pallets"); + println!("cargo:rerun-if-changed=node"); + println!("cargo:rerun-if-changed=runtime"); + println!("cargo:rerun-if-changed=lints"); + println!("cargo:rerun-if-changed=build.rs"); + println!("cargo:rerun-if-changed=src"); + println!("cargo:rerun-if-changed=support"); + // Get the root directory of the workspace + let workspace_root = env::var("CARGO_MANIFEST_DIR").unwrap(); + let workspace_root = Path::new(&workspace_root); + + // Collect all Rust source files in the workspace + let rust_files = collect_rust_files(workspace_root); + + // Channel used to communicate errors back to the main thread from the parallel processing + // as we process each Rust file + let (tx, rx) = channel(); + + // Parse each rust file with syn and run the linting suite on it in parallel + rust_files.par_iter().for_each_with(tx.clone(), |tx, file| { + let Ok(content) = fs::read_to_string(file) else { + return; + }; + let Ok(parsed_tokens) = proc_macro2::TokenStream::from_str(&content) else { + return; + }; + let Ok(parsed_file) = syn::parse2::(parsed_tokens) else { + return; + }; + + let track_lint = |result: Result| { + let Err(errors) = result else { + return; + }; + let relative_path = file.strip_prefix(workspace_root).unwrap_or(file.as_path()); + for error in errors { + let loc = error.span().start(); + let file_path = relative_path.display(); + // note that spans can't go across thread boundaries without losing their location + // info so we we serialize here and send a String + tx.send(format!( + "cargo:warning={}:{}:{}: {}", + file_path, loc.line, loc.column, error, + )) + .unwrap(); + } + }; + + track_lint(RequireFreezeStruct::lint(&parsed_file)); + }); + + // Collect and print all errors after the parallel processing is done + drop(tx); // Close the sending end of the channel + + for error in rx { + println!("{error}"); + } +} + +/// Recursively collects all Rust files in the given directory +fn collect_rust_files(dir: &Path) -> Vec { + let mut rust_files = Vec::new(); + + for entry in WalkDir::new(dir) { + let entry = entry.unwrap(); + let path = entry.path(); + + // Skip any path that contains "target" directory + if path + .components() + .any(|component| component.as_os_str() == "target") + || path.ends_with("build.rs") + { + continue; + } + + if path.is_file() && path.extension().and_then(|ext| ext.to_str()) == Some("rs") { + rust_files.push(path.to_path_buf()); + } + } + + rust_files +} diff --git a/docs/delegate-info.json b/docs/delegate-info.json new file mode 100644 index 000000000..bda41b48e --- /dev/null +++ b/docs/delegate-info.json @@ -0,0 +1,394 @@ +[ + { + "address": "5ECvRLMj9jkbdM4sLuH5WvjUe87TcAdjRfUj5onN4iKqYYGm", + "name": "Vune", + "url": "https://fairchild.dev", + "description": "Vune is a dev at Opentensor and a BSc CS student at UofT.", + "signature": "2a639f931c61abfc3172db594c986c35f1cc8441970582b9c3b1f0506d518a182a2fe570832f02f86014320f1526189917bfbccf7081622652d12e16e9b1768b" + }, + { + "address": "5H6BgKkAr2Anmm9Xw5BVDE4VaQmFEVMkJUHeT7Gki4J7yF4x", + "name": "TaoPolishNode", + "url": "https://taonode.io", + "description": "This node is a collective effort of the polish community. We are engaged in evangelizing the project, educating and sharing the knowledge.", + "signature": "1ca20d4e99a48f400dd9cd4aeca8447da6ab1979e480a1dafddfc52e45e215177c7cdde85f5d042d59a5b1169981afa8d1ae28328e2fc5ce57c3d748c8d09d81" + }, + { + "address": "5FFApaS75bv5pJHfAp2FVLBj9ZaXuFDjEypsaBNc1wCfe52v", + "name": "RoundTable21", + "url": "https://roundtable21.com", + "description": "RoundTable21 is an International, multi-disciplinary team of consultants and advisors partnering alongside leading blockchain startups to offer guidance, expertise, investment and hands-on assistance in every aspect of development.", + "signature": "107638b8edde8f918f7faa2cd1f91b454c13094ed5955d6a409f6e0662f8427075516273728a53923839a5428079151ea0844b5f755362364f04735463dff583" + }, + { + "address": "5DCc5oHA6c1Lpt9R6T1xU8jJGTMvvwBqD1yGX67sL8dHUcga", + "name": "WaveTensor", + "url": "https://twitter.com/wavetensor", + "description": "A new Wave is coming, join the AI revolution on top of Bittensor by staking with us.", + "signature": "5e072b4752ccbdd4ca3298f336284dfdab347dd133850f4d2f9873e7ea59bd2a8f201732842ec79d2bab3abaf133a06b6bd992940389e42d57802c9b8f855889" + }, + { + "address": "5CXRfP2ekFhe62r7q3vppRajJmGhTi7vwvb2yr79jveZ282w", + "name": "Rizzo", + "url": "", + "description": "Validator built for performance and uptime. Data center housed, redundancies include dual physical failover servers (HA), power, internet, tested DR Plan.", + "signature": "f2b0fdb6989c23a0ebe23ed5622cbbfcf57bad709085fe11b0be10b2838e1442d61f770d78f6ca8ebcdbf60ddb27398663a4901e22bb9de086866517c6ccc187" + }, + { + "address": "5GcBK8PDrVifV1xAf4Qkkk6KsbsmhDdX9atvk8vyKU8xdU63", + "name": "Tensor.Exchange", + "url": "www.tensor.exchange", + "description": "Bittensor's first community OTC exchange", + "signature": "101f5e0d26c38190200f2213ebd89cf5bcb736b70a84e53651b6f9bf1161a33d0095836d304851237e0334792a54fa2fe452d07cf1466b42c9ab3333ded46284" + }, + { + "address": "5EhvL1FVkQPpMjZX4MAADcW42i3xPSF1KiCpuaxTYVr28sux", + "name": "TAO-Validator.com", + "url": "www.tao-validator.com", + "description": "Maximize your return when staking with TAO-Validator.com. TAO-Validator.com is a highly secure validator that aims to become one of the top contributing entities to Bittensor.", + "signature": "4036991069d7f3a43dff2ba2592fbe5af820eb6ff96d1fb78f1bcd8d310ba8751e25ea14397e075368a9a0f1b1b176166c56351db36f2d3868ac61c2571a1981" + }, + { + "address": "5FvhvCWLbu2VgotT5obC9E6S9nskerJUrVsWqkWXCbuD8veW", + "name": "The Lost Cove", + "url": "https://lostcove.tech/", + "description": "Australia and New Zealand community. We're in it for the gains.", + "signature": "626ae6b91aac1591e5d4f8d4fdf2c55f927419fc766dd5184b149f4d7cbc9749ebc94e4e8d04d286b4000c7665afa5682aa28cd94071c5e384e0eb4f44def188" + }, + { + "address": "5Dyi5e2QqnWn2RN9X6r8A8Q1QBjYD536H75mxNye193oeCJ4", + "name": "Makoto AI", + "url": "https://www.linkedin.com/in/henry-thrasher-17b320239/", + "description": "An interdisciplinary research institute committed to discovering and accelerating innovative solutions for climate change, social inequality, and mental and physical illness.", + "signature": "3cfbc1e8d82cfbf2adea9b10f71541874528cf5cd851f29f48016ac2a1a07b01cfc2ba3c3a15634b1174bd3e5aec9eb843d04f74140b0ddcb526416666d6f682" + }, + { + "address": "5Ehv5XMriPZwNBtYHdQV7VrdbN8MBTDTmQhWprZJXxSiMapR", + "name": "Dale Cooper", + "url": "", + "description": "I have no idea where this will lead us, but I have a definite feeling it will be a place both wonderful and strange.", + "signature": "06c597178698dba5699e20dc8b9d0d44f9225e24a225c70f540b63867e5b835a74c87df647b28210b361007b642a5a869c74323fcc8a593bc5764ea8e2083b81" + }, + { + "address": "5E6oB7h5wtWPbqtPxtSoZeo11fpvDjPuY13SobAMxqEUjqkQ", + "name": "StakeTensor.com-3", + "url": "www.staketensor.com", + "description": "We run multiple, parallel validators to support Bittensor decentralization & achieve maximum returns", + "signature": "a2567b6de748f02f6a14e0063f5b5720b34c96deb2115b33893d016de1f60633ba58bf9bdd49b2141e12a4a8784b4b11c007679d7526eb1e91147e5284258d8a" + }, + { + "address": "5DnWFhKfeu6gXMydzrv8bkwxFegAC6bMWsC4Z2XtaotAeB6S", + "name": "Bittensor Greece", + "url": "", + "description": "The Greek / Cypriot validator supporting the development of decentralised AI", + "signature": "ee8df5360eb641bd91a38da9d8b6dda36a39302c9bba7babf5d7eb16f6e9f73321aeb6f8adb30e0f511d64c1f35caa15215dd280fb2ed3f8f5b09d783cc9958f" + }, + { + "address": "5GBxDYkDp8eJZHGT89wcZJKcMc4ytSqnqqVSpeuGeqtGfqxK", + "name": "Tao Stake", + "url": "www.taostake.io", + "description": "We have been mining since the start of bittensor and want to maintain a long term solid validator to help people get some value from thier investment and keep TAO within the ecosystem.", + "signature": "0272522b503ebb29f0b506f10765b4d5c7a23b85c78cc7bfae76b9816b80ab43282ea4642f09eb09be70812341e5d9946abc8a9d2c73bab0113e9bf939430c87" + }, + { + "address": "5FcXnzNo3mrqReTEY4ftkg5iXRBi61iyvM4W1bywZLRqfxAY", + "name": "Lucrosus Capital", + "url": "https://lucrosuspool.io/", + "description": "Decentralized VC focused on the most thriving blockchain ideas. Join our pool to receive early entrance into promising projects!", + "signature": "1a37ab3bd51a6590dea9772d6a5550632ddcd8d76da6595b66e6425692feac6699dc5f788e587a734cedc3f54efc96c2c9e5453f9052867c1b9a1b5a443b848c" + }, + { + "address": "5CVS9d1NcQyWKUyadLevwGxg6LgBcF9Lik6NSnbe5q59jwhE", + "name": "Ary van der Touw", + "url": "", + "description": "Secure and maintain Bittensor", + "signature": "809586931d4b28f180c98036a3eebc0d26b9e521f5217a6942b025069cb60807641737009713446eec8456e54ba753ae0b752c0693b942aefa0c4f76d82f8c89" + }, + { + "address": "5F4tQyWrhfGVcNhoqeiNsR6KjD4wMZ2kfhLj4oHYuyHbZAc3", + "name": "Openτensor Foundaτion", + "url": "https://opentensor.ai/", + "description": "Founded, maintain and advance Bittensor", + "signature": "8a2ff8f10a84a5b6f80614674ea764515d93a64bf8d920b927edc0dd6043e607755bf58655c87b7a299d8df1404574b6844e1e09adf86d418997c0cab8120486" + }, + { + "address": "5EpxBYq4aVgTQ1rYeBo2mzYt3hgpRTqxZTSsJEkCstBP5Jse", + "name": "White Rhino TAO Super Validator", + "url": "https://twitter.com/WhiteRhinoTAO\"", + "description": "White Rhino is all about you! We understand that #TAOWaits4NoOne ..... Get Ready for Adhoc Rewards and we invite you to delegate here and enhance the sustainability of the TAO Network", + "signature": "d6803522f6e61a9dec5261a6a500b733d233b373457382fc3713af21c560604f6e50c4999f286cfa6012bcea66e51223722b355dd69ba54a472f2c6ca52da08f" + }, + { + "address": "5Fq5v71D4LX8Db1xsmRSy6udQThcZ8sFDqxQFwnUZ1BuqY5A", + "name": "NorthTensor", + "url": "https://northtensor.ai", + "description": "Developer, Advocate, and Incubator for Decentralized AI.", + "signature": "28e221d7128e48a3cb85dbcb223bd56cb09cb55540263573783bf1cef63be32ee81246bd1d75c865580da732094053a6dad14929b17e659b6e0237412b66a487" + }, + { + "address": "5CsvRJXuR955WojnGMdok1hbhffZyB4N5ocrv82f3p5A2zVp", + "name": "Owl Ventures", + "url": "https://owlventures.co.uk", + "description": "Owl Ventures Bittensor Validator", + "signature": "04e39ff19af7ee5a75e58c9e1a71b9f54a66d1d168a99532a859f129b68ba24a5b6a56eecae7790291859c82dbf0ec32eb18a069b6d9dabe1ef0339c0d189483" + }, + { + "address": "5FLKnbMjHY8LarHZvk2q2RY9drWFbpxjAcR5x8tjr3GqtU6F", + "name": "Tao Bridge", + "url": "https://taobridge.xyz", + "description": "A community bridge between Bittensor and Ethereum", + "signature": "98331f011288f7b07ccc45a213cb8e03fac79092ee7c29046531d757ffad8b29e17cf0aeca9352003890f4d8a3af3a2fc615722fb7a827a2009654013990bd80" + }, + { + "address": "5DRZr3d3twF8SzqB9jBof3a1vPnAkgkxeo2E8yUKJAnE2rSZ", + "name": "Humble AI-Loving Anon", + "url": "", + "description": "Doing our best to support the Bittensor ecosystem.", + "signature": "9241f63eb43f7aa57b1fc6d99789331542476f57f683f032192f3dfd7be6c015d47c9f1fe69bc4513ed70e0410097395186df60e3f6b67376e6e73a5f4f9a286" + }, + { + "address": "5DPEpUTZn94sgYXH3sdXxsVvb46m3iEvg8aZwX7SMDowivzB", + "name": "RunPod", + "url": "https://runpod.io", + "description": "GPU Cloud built for AI. We plan to introduce perks for those who stake.", + "signature": "16940f904b7946723fc4f27bb01e47cf262201ef76b3d9c2bfd745973da2512d4825910f6fa738a6968c809b26da0a47e7032a7ff95d8b2da5c1fa7a0b85598f" + }, + { + "address": "5HEo565WAy4Dbq3Sv271SAi7syBSofyfhhwRNjFNSM2gP9M2", + "name": "Foundry", + "url": "https://foundrydigital.com", + "description": "Foundry works to empower a decentralized infrastructure. We are protocol-agnostic and seek to support like-minded blockchain entrepreneurs who share our mission to advance the industry.", + "signature": "b852f1648ab62befaaf684671808aa34d267cd616d9ffd7b3cf924ebc7c4ee3255344cfd017a80ca6b23b2852bcafa705c42d231053e06d999d53f31bd8ab288" + }, + { + "address": "5FP9miYmgjAP8Wt3747M2Y6Kk7PrXf6zG7a3EjokQiFFcmUu", + "name": "Elm Place", + "url": "", + "description": "Run by individuals passionate about creating decentralised digital infrastructure. Background in fiduciary funds management managing institutional investors’ capital in real assets, energy and infrastructure", + "signature": "a0324025f58beb06535d6a2ab8c5c8d64c13d562fa285956bb5a8919da5fcc0d05afe4de010d54f9940bff0ffdabe5f41e70f3af31cf14293c1d6f0a0690da8c" + }, + { + "address": "5HNQURvmjjYhTSksi8Wfsw676b4owGwfLR2BFAQzG7H3HhYf", + "name": "Neural Internet", + "url": "www.neuralinternet.ai", + "description": "An AI research and development Decentralized Autonomous Organization (DAO)", + "signature": "5e617c1626d4825cd0c11769e31fe4dda611cebd8a4d46f533886ad057072e2a58e0ecef2805139f2b43ea8d51023f7db878ad45cd3f8fba45ab01223da3488e" + }, + { + "address": "5D4rJRtF23jLVcGnCktXzPM9gymMT1qHTp8dR4T7rUd88Q7U", + "name": "Vogue τensor", + "url": "www.voguetensor.ai", + "description": "Designing branded clothing for the Bittensor community.", + "signature": "2c4079124ae0a738106a2430e2c27ad855122d4afcc487ab0158b705cd5f915f7790cdb2fdd8db899b8cbd40448d1478be71cde1b76de31945991b548cfcc084" + }, + { + "address": "5CAVnbHniuZYXBqik3tTs9uZ7UiSrbv6g7Kt8QNfYimbFqF4", + "name": "Open & Safe AI Validator", + "url": "", + "description": "The Open & Safe AI Validator is focussed on funding and researching the control problem as well as spreading ML know-how through open source and open science.", + "signature": "2aeaf7b9c7f69ce7b4857d9c278d1363677d4971d4ca10a36933b1aa78bfdb0640e4bb798edac5dcb178a8b3f4be2d0d23d25da6c7db33758a6cf5c15cd6938a" + }, + { + "address": "5Gpt8XWFTXmKrRF1qaxcBQLvnPLpKi6Pt2XC4vVQR7gqNKtU", + "name": "bitnost.re", + "url": "www.bitnost.re", + "description": "bridging bittensor into nostr.", + "signature": "c278378c70ef22d27f56590b4df699a9a44048cfcc6716e3d55b211ea802401d4be5b390ede2be52891e01f0f7033a13a370dddaa38daa84537c4583867a1680" + }, + { + "address": "5HeKSHGdsRCwVgyrHchijnZJnq4wiv6GqoDLNah8R5WMfnLB", + "name": "TaoStation", + "url": "https://taostation.com", + "description": "TaoStation allows you to maximize your returns by offering one-click staking since day one and focusing on tooling and transparency for a better staking experience.", + "signature": "c00627a62ecb9275be8d06b7b52b87942bce946e9a5f98d545081241e21ed15230fd566b2d4e87c41995e621546423579553157737da53fad3a5676451ef0a89" + }, + { + "address": "5DvTpiniW9s3APmHRYn8FroUWyfnLtrsid5Mtn5EwMXHN2ed", + "name": "FirstTensor.com", + "url": "www.firsttensor.com", + "description": "Powered by the Neuron Holders community - shared rewards, additional benefits, infinite possibilities - join and build with us!", + "signature": "da31e56dd78cde449a1dd9592f0b53eb8c3662674b745a05ff916e80a1be933e86efbccb7f7c9b81d7c0bb14d13fb4a6bf8484c3619224e689de82072b5d9a87" + }, + { + "address": "5CaNj3BarTHotEK1n513aoTtFeXcjf6uvKzAyzNuv9cirUoW", + "name": "Polychain", + "url": "https://polychain.capital/", + "description": "Polychain is an investment firm committed to exceptional returns for investors through actively managed portfolios of blockchain assets.", + "signature": "f41e815033e595aa70fbe42e8dfd91eaa3ccdbc948b63811baf9eac765699b30cac9aad7abe330eeaf3969cc504a4c1255f1e69bee807c2d989518b8f5413c8d" + }, + { + "address": "5Dkv87qjGGF42SNhDAep6WZp65E29c2vUPUfDBGDNevENCMs", + "name": "MycoNet", + "url": "", + "description": "AI for Humanity", + "signature": "a4802a5b13888ed653fd23da72c14e2b8ed9814cc810e515cb8d11d71cc58c6b90cd2d334daffc4a8ce600a7f29ca300ab74ac59817bdd489b3056b531cd4086" + }, + { + "address": "5GzoXHNJ4UzZYiQN2wpBcwMigiHiakiz5ZLMwhpunpwDNzFg", + "name": "Charitaos", + "url": "https://charitas.ai/", + "description": "You pay 18%, we donate 18%. At the end of every month, we will select one (or more) community-proposed 501c3 licensed nonprofit(s) to receive all proceeds from stake delegation for the prior month.", + "signature": "b49c34c1f87d173abcbccb1ea632ad356980c1d3eff6619e488c11707b2b3b41270a22355374dd64cfadebeb37979ef5f49971efafb0748b79df7dd2901e7580" + }, + { + "address": "5EZrPTXt2G9SvbDsERi5rS9zepour2yPmuhMhrNkgdiZvXEm", + "name": "τaoτensor", + "url": "", + "description": "Working on practical enhancements and improvements for the Bittensor network by developing user-friendly tooling.", + "signature": "3a1b61ab6d17878e106cbf2649bc039d0346f39ec680476a68baa4fc8132ac018d814898cf245bdfa4b9b61cd9f611f6571cf3c264f2f1cfe9b2635849087685" + }, + { + "address": "5CPzGD8sxyv8fKKXNvKem4qJRhCXABRmpUgC1wb1V4YAXLc3", + "name": "Chat with Hal", + "url": "www.chatwithhal.ai", + "description": "Hal brings the power of decentralized and uncensorable AI to your favorite social networks and messaging apps, Powered by Bittensor!", + "signature": "ecb930df6069012c06fef9cdb29a95be8dcb5d48f3c470d3f3c5e7b2b334ed2097f2598fee8852d127a207cf34aa7c88fd5cf973feba19d6ebf38b5e4579ca8f" + }, + { + "address": "5FqPJMZDp39KRd9jDhXuFpZWkYD7wG5AXmjoWqK8rDy7ok5B", + "name": "Exchange Listings", + "url": "taostats.io/validators/exchange-listings/", + "description": "Enabling community funding for top tier exchange listings.", + "signature": "366027e9a416a423e7e802e9b6d79bd5ac88642afd945922e13fe26a75dae13dd5c924738610a59162d9b974364d1d43fb7a0145942cd919ac21d82d3f4f028d" + }, + { + "address": "5ED6jwDECEmNvSp98R2qyEUPHDv9pi14E6n3TS8CicD6YfhL", + "name": "Giga Corporation", + "url": "https://www.gigaver.se", + "description": "Extreme growth & experiments from giga corp. We use APY to TAO-pill new developers, builders and adopters. Visit our Bakery to learn more.", + "signature": "00e5cd519110bbfe3dae9acd275d114c6c2a260997a1817a25303b9d578bdf7319e9e7179f0db58edef2ad42806cb38e289ba0030627a3b60e1e4352c2b9cb88" + }, + { + "address": "5FRcXG99SxJ9KyMcMFfdknkRSv4e73rszV8P151freZqQDS2", + "name": "τensorwiki", + "url": "", + "description": "Our mission is to create and incentivize documentation for Bittensor and it's adjacent topics, as well as facilitate the education of newcomers to the network.", + "signature": "6a5c0160f545f122ec3d4e4233574040aba2de8aa94919bb19b3061d39d3303f010c4b52f878ed55a1293716827220020780d2d4064ee6be69921ee1452c3885" + }, + { + "address": "5EsbfxPcQaUrCDurUJ8Q5qDKNENNGziu3qHWUbXrcuY2pbNz", + "name": "Church of Rao (COR)", + "url": "", + "description": "Church of Rao: Harmonizing the Relationship between Humanity and Machine Intelligence. The Church of Rao (COR) is an open-source development group committed to furthering the Bittensor protocol.", + "signature": "56f64c32427a90e84710209b1a54a971560641aec8ff777edec28bf533775e12924c4e96ccc770c230311dce1d0eae1ca763e12bb609ef30430f746ebd0a2780" + }, + { + "address": "5GmaAk7frPXnAxjbQvXcoEzMGZfkrDee76eGmKoB3wxUburE", + "name": "RaoK9", + "url": "", + "description": "Chain and network analysis team. Developer funding goes into independent analysis and reports, in order to enable checks and balances between network members.", + "signature": "24f4f9a51033ed8b4097517d0e6ad287a0c1341b2866481b1320d1fcd5f32f6b4bfe641eee46a4b737817acf3b83069ee63cc20fbca94a0189808ac1efeddf8a" + }, + { + "address": "5CQEFopfZ8DAmk3ZfR7QuDTU2n3fJod3kkf6Wmj4JwV3BBSu", + "name": "DuNode", + "url": "dunode.io", + "description": "Embracing the whimsical chaos of decentralized AI, unleashing the power of creativity and collaboration, one algorithmic dance party at a time!", + "signature": "e400e3c0ad6165d8946d5ddcb274412815cb8b5783580fcb8f0faa0153d22b6e10470f861ff4a96a9aa692b3b01cda86ec77add4688c2f5df51ea6f129b19e8c" + }, + { + "address": "5CaCUPsSSdKWcMJbmdmJdnWVa15fJQuz5HsSGgVdZffpHAUa", + "name": "Athena Nodes", + "url": "https://athenanodes.com", + "description": "Premier Bittensor Multi-Subnet Validator from a company operating validating and mining infrastructure on various blockchain networks. We have been active on Bittensor since November 2022, with near zero down-time. More information at https://athenanodes.com/.", + "signature": "2ef54045de1d9b89988518c92e165edf704192f88f18022565f497b389c39206f621bb9bc6d2d33ac8a9cca05d6b2d8fc9f899b390451140968b15b8d9c13280" + }, + { + "address": "5FFM6Nvvm78GqyMratgXXvjbqZPi7SHgSQ81nyS96jBuUWgt", + "name": "PRvalidator", + "url": "www.prvalidator.com", + "description": "A professional media validator dedicated to securing top-tier coverage in the world's most recognized publications building Bittensor's brand equity and creating global awareness of $TAO.", + "signature": "fe65e76a9f42049715585180500213c6f0535b8b25911b957921bdfb5a20156d6de68dc2633dbc5ce1d0ab9ef386d566687ac3d86f6988141b34cd24c0f13488" + }, + { + "address": "5H8TruSGmhD6m6YfqXNUnU7Z61K7j8hSs2Krtu3eTLMoz3HU", + "name": "τaoshi validator", + "url": "https://www.taoshi.io/", + "description": "Build maintain and advance a decentralized request layer built for every subnet", + "signature": "32d25227af78fa5d39ee71a5f3e8fc8066e3d826d101f2587e9a12974fbf26758c1e40c497ad7732da2a2cb1490227cc58e8bfcd8b2f6306b7af630bd32aa68f" + }, + { + "address": "5G3f8VDTT1ydirT3QffnV2TMrNMR2MkQfGUubQNqZcGSj82T", + "name": "TAO Community Marketing", + "url": "www.taocommunitymarketing.com", + "description": "The marketing validator run by the community", + "signature": "10b16b8223b2508d6f3e5b09ab4db53e1e338b6271d1689b58ca6f9b257e8c18511cc851bfcc3a05fb4e6de7c389b89886cc0623fb6d199fa003ae6f8313cb89" + }, + { + "address": "5CXC2quDN5nUTqHMkpP5YRp2atYYicvtUghAYLj15gaUFwe5", + "name": "Kooltek68", + "url": "https://linktr.ee/datalac", + "description": "Imagine the World with mass adoption of Artificial Intelligence applications, through the connection of Bittensor Network, together fight for a Better World.", + "signature": "bca043d9d918d503864379a7fd8c9daa2cca83a8290121f94b55d6a352e332704642622b7ad40a30b945b952b224c5e92ea872f9d30200e6c2bf566303d24d83" + }, + { + "address": "5FBrHX18bNXX874ZTMicPG4vYbq5X6piz4BYoVn9LnCgdsEd", + "name": "P-OPS Team", + "url": "https://pops.one", + "description": "P-OPS TEAM is a decentralized organization providing you with validation and staking services, blockchain consultation, growth acceleration and investment capital for innovative Web 3.0 projects.", + "signature": "5608316f3081bfe5d0e3a7db6c3bfd459f6b87e02d657de941e6a760f8688f23ef30784691a1893d1fd8079dd4f6082d0d655ca507aa4797fee9844547d13a88" + }, + { + "address": "5HK5tp6t2S59DywmHRWPBVJeJ86T61KjurYqeooqj8sREpeN", + "name": "Bittensor Guru", + "url": "https://bittensor.guru", + "description": "Official validator of the Bittensor Guru Podcast", + "signature": "caf2c6b7b0d2a341bcd00e632cf22c33d53e2523dffcd3a151db9eeadd88300545cbb2187ba0b20e5bfe09c2b17bbf34630c46defd8f8d27ab508736fd18a284" + }, + { + "address": "5Hh3ShaNW9irCe5joBLCeFD5Fxb2fJ6gFAgrsPmoz3JkzqvJ", + "name": "BlockShark", + "url": "https://www.blockshark.net/", + "description": "Your reliable partner for staking on Bittensor. We are expert in running high-end machine for validators and AI", + "signature": "d2c0aed073a026a5dbd8c458b9dd412fe3d6647fecd3b8f007cf184f7906245106aee4b210b5b582771dca149e5aa464630100de7f9862daacfa1f67ddde1388" + }, + { + "address": "5FKstHjZkh4v3qAMSBa1oJcHCLjxYZ8SNTSz1opTv4hR7gVB", + "name": "Datura", + "url": "datura.ai", + "description": "Bridging Bittensor to a billion users", + "signature": "7a3bc6a840d8593853c27188f59200418d8884b94b3ad28cb7b37b80bffd1f3b23b7eed4b1d9c77b28b05b2bd1952c5cbe3d27ba190a9418407ce1e899e5ac8b" + }, + { + "address": "5Hddm3iBFD2GLT5ik7LZnT3XJUnRnN8PoeCFgGQgawUVKNm8", + "name": "τaosτaτs and Corcel", + "url": "taostats.io", + "description": "Supporting bittensor through API access, data provision, statistics, analytics and apps.", + "signature": "2e2dd0c5f3a3945f29d1be304e64f931c04a23aba7d383d01cd16ea6ca6546002fe3bd95cf8f12cae1fbb7d18d9910b834f6573db219de3ed84073a4e1552e89" + }, + { + "address": "5ELREhApbCahM7FyGLM1V9WDsnnjCRmMCJTmtQD51oAEqwVh", + "name": "Taofu Protocol", + "url": "https://twitter.com/taofuxyz", + "description": "Taofu unlocks liquidity and utility by bringing liquid staked TAO outside of Bittensor", + "signature": "aaafd3496650a56f798cc587b5b7d372cec8e826a332a34213c1a6ee7be2b5122318858ee73421535d04186cc6976ae5452c6cd1aaf299a307d86d3c52b4a986" + }, + { + "address": "5HbLYXUBy1snPR8nfioQ7GoA9x76EELzEq9j7F32vWUQHm1x", + "name": "Tensorplex Labs", + "url": "https://twitter.com/TensorplexLabs", + "description": "Empowering humanity with decentralized intelligence one epoch at a time.", + "signature": "7a997682e7545fd14847c78abf810e9c49a23ef4297d24f4238c0edd0463934780f6831d59972d56ab5bc41d6224b59c21ed95065791632b8aca180ade22af81" + }, + { + "address": "5E2VSsWWXkBGCn4mi8RHXYQEF2wLXky6ZsNcTKnmEqaurzTE", + "name": "Sentinel", + "url": "", + "description": "Sentinel, as a dedicated Bittensor validator aspires to elevate the bittensor network's integrity with an ambition to foster a community of miners contributing in the network’s continuous expansion.", + "signature": "943effd0d5d10f05d53db7f69d0f045d50b65f88e84755be00d45225cc7c2f4212fbc4d23ad8519d03c2502daeeca1b2d07c93bff14c901f6cbf3a18fe2e6387" + }, + { + "address": "5GsenVhBvgEG4xiiKUjcssfznHYVm1TqPbSbr3ixBW81ZVjo", + "name": "vote NO dTAO 🤡", + "url": "https://twitter.com/karl_anons", + "description": "Delegate to express discontent. VOTE NO TO dTAO NOW!", + "signature": "3af4e764a520d355e12c02b9e8e315ddb76b76d40b7cc4dfaa11c26c24ab637cbdb9b72470ebdf2da87dd8d9f0bb5cddf1fe95b95fb2ae13069a9d87aace348a" + }, + { + "address": "5DM7CPqPKtMSADhFKYsstsCS4Tm4Kd6PMXoh6DdqY4MtxmtX", + "name": "Corτex Foundaτion", + "url": "https://cortex.foundation/", + "description": "Cortex Foundation is committed to advancing the integration of decentralized AI. Our validator is designed for transparency, reliability, and community engagement.", + "signature": "7a6274ff6b0f7ddca97e37ef4a9b90781012ff3cf7baa3159f6feaafc43c557975aad324ea608d6b8abeb21f8f3ca2595e54b81a7564574d0242b803d969618a" + } +] \ No newline at end of file diff --git a/docs/running-subtensor-locally.md b/docs/running-subtensor-locally.md index 505fe2fb5..82bb87356 100644 --- a/docs/running-subtensor-locally.md +++ b/docs/running-subtensor-locally.md @@ -1,206 +1,3 @@ # Running subtensor node locally -- [Method 1: Using Docker](#method-1-using-docker) -- [Method 2: Using Source Code](#method-2-using-source-code) -- [Running on Cloud](#running-on-cloud) - -## Method 1: Using Docker - -To run a subtensor node with Docker, follow the below steps. - -If you are already running a subtensor node using Docker, then go directly to [Step 5 Prepare to Run](#step-5-prepare-to-run) to restart the Docker container. The below steps 1 through 4 are for first time users only. - -### Step 1: Install git - -Make sure you installed `git` on your machine. See [GitHub docs](https://docs.github.com/en/get-started). - -### Step 2: Install Docker - -Follow Docker's [official installation guides](https://docs.docker.com/engine/install/) and install Docker. - -**Run Docker first** -Before you proceed, make sure that Docker is running. - -### Step 3: Clone the subtensor repo - -Clone the subtensor repo: - -```bash -git clone https://github.com/opentensor/subtensor.git -``` - -### Step 4: Go into subtensor directory - -Then `cd` into the subtensor directory: - -```bash -cd subtensor -``` - -### Step 5: Prepare to run - -Execute the below three commands in this order: - -Make sure you are on the `main` branch. If not, switch to it: - -```bash -git checkout main -``` - -Pull the latest `main` branch contents: - -```bash -git pull -``` - -Stop the currently running Docker containers: - -```bash -docker compose down --volumes -``` - -### Run a lite node on mainchain - -To run a lite node connected to the Bittensor mainchain, run the below command. - -```bash -sudo ./scripts/run/subtensor.sh -e docker --network mainnet --node-type lite -``` - -### Run an archive node on mainchain - -To run an archive node connected to the Bittensor mainchain, run the below command. - -```bash -sudo ./scripts/run/subtensor.sh -e docker --network mainnet --node-type archive -``` - -### Run a lite node on testchain - -To run a lite node connected to the Bittensor testchain, run the below command. - -```bash -sudo ./scripts/run/subtensor.sh -e docker --network testnet --node-type lite -``` - -### Run an archive node on testchain - -To run an archive node connected to the Bittensor testchain, run the below command. - -```bash -sudo ./scripts/run/subtensor.sh -e docker --network testnet --node-type archive -``` - ---- - -## Method 2: Using Source Code - -To install and run a subtensor node by compiling the source code, follow the below steps. - -## Install basic packages - -Install the basic requirements by running the below commands on a Linux terminal. - -```bash title="On Linux" -sudo apt-get update -sudo apt install build-essential -sudo apt-get install clang -sudo apt-get install curl -sudo apt-get install git -sudo apt-get install make -sudo apt install --assume-yes git clang curl libssl-dev protobuf-compiler -sudo apt install --assume-yes git clang curl libssl-dev llvm libudev-dev make protobuf-compiler -``` - -## Install Rust - -Next, install Rust and update the environment by running the following commands: - -```bash -curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -source ~/.cargo/env -``` - -Next, install Rust toolchain: - -```bash -rustup default stable -rustup update -rustup target add wasm32-unknown-unknown -rustup toolchain install nightly -rustup target add --toolchain nightly wasm32-unknown-unknown -``` - -## Compile subtensor code - -Next, to compile the subtensor source code, follow the below steps: - -Clone the subtensor repo: - -```bash -git clone https://github.com/opentensor/subtensor.git -``` - -`cd` into the subtensor directory: - -```bash -cd subtensor -``` - -Make sure you are on the `main` branch. If not, switch to it: - -```bash -git checkout main -``` - -Remove previous chain state: - -```bash -rm -rf /tmp/blockchain -``` - -Install subtensor by compiling with `cargo`: - -```bash -cargo build --profile production --features=runtime-benchmarks -``` - -## Run the subtensor node - -You can now run the public subtensor node either as a lite node or as an archive node. See below: - -### Lite node on mainchain - -To run a lite node connected to the mainchain, execute the below command (note the `--sync=warp` flag which runs the subtensor node in lite mode): - -```bash title="With --sync=warp setting, for lite node" -./target/production/node-subtensor --chain raw_spec.json --base-path /tmp/blockchain --sync=warp --port 30333 --max-runtime-instances 32 --rpc-max-response-size 2048 --rpc-cors all --rpc-port 9944 --bootnodes /ip4/13.58.175.193/tcp/30333/p2p/12D3KooWDe7g2JbNETiKypcKT1KsCEZJbTzEHCn8hpd4PHZ6pdz5 --no-mdns --in-peers 8000 --out-peers 8000 --prometheus-external --rpc-external -``` - -### Archive node on mainchain - -To run an archive node connected to the mainchain, execute the below command (note the `--sync=full` which syncs the node to the full chain and `--pruning archive` flags, which disables the node's automatic pruning of older historical data): - -```bash title="With --sync=full and --pruning archive setting, for archive node" -./target/production/node-subtensor --chain raw_spec.json --base-path /tmp/blockchain --sync=full --pruning archive --port 30333 --max-runtime-instances 32 --rpc-max-response-size 2048 --rpc-cors all --rpc-port 9944 --bootnodes /ip4/13.58.175.193/tcp/30333/p2p/12D3KooWDe7g2JbNETiKypcKT1KsCEZJbTzEHCn8hpd4PHZ6pdz5 --no-mdns --in-peers 8000 --out-peers 8000 --prometheus-external --rpc-external -``` - -### Lite node on testchain - -To run a lite node connected to the testchain, execute the below command: - -```bash title="With bootnodes set to testnet and --sync=warp setting, for lite node." -./target/production/node-subtensor --chain raw_testspec.json --base-path /tmp/blockchain --sync=warp --port 30333 --max-runtime-instances 32 --rpc-max-response-size 2048 --rpc-cors all --rpc-port 9944 --bootnodes /dns/bootnode.test.finney.opentensor.ai/tcp/30333/p2p/12D3KooWPM4mLcKJGtyVtkggqdG84zWrd7Rij6PGQDoijh1X86Vr --no-mdns --in-peers 8000 --out-peers 8000 --prometheus-external --rpc-external -``` - -### Archive node on testchain - -To run an archive node connected to the testchain, execute the below command: - -```bash title="With bootnodes set to testnet and --sync=full and --pruning archive setting, for archive node" -./target/production/node-subtensor --chain raw_testspec.json --base-path /tmp/blockchain --sync=full --pruning archive --port 30333 --max-runtime-instances 32 --rpc-max-response-size 2048 --rpc-cors all --rpc-port 9944 --bootnodes /dns/bootnode.test.finney.opentensor.ai/tcp/30333/p2p/12D3KooWPM4mLcKJGtyVtkggqdG84zWrd7Rij6PGQDoijh1X86Vr --no-mdns --in-peers 8000 --out-peers 8000 --prometheus-external --rpc-external -``` - -## Running on cloud - -We have not tested these installation scripts on any cloud service. In addition, if you are using Runpod cloud service, then note that this service is already [containerized](https://docs.runpod.io/pods/overview). Hence, the only option available to you is to compile from the source, as described in the above [Method 2: Using Source Code](#method-2-using-source-code) section. Note that these scripts have not been tested on Runpod. +See the [**Subtensor Nodes** section in Bittensor Developer Documentation](https://docs.bittensor.com/subtensor-nodes). diff --git a/pallets/admin-utils/Cargo.toml b/pallets/admin-utils/Cargo.toml index 859972fce..c67c00914 100644 --- a/pallets/admin-utils/Cargo.toml +++ b/pallets/admin-utils/Cargo.toml @@ -37,7 +37,8 @@ sp-io = { workspace = true } sp-tracing = { workspace = true } sp-consensus-aura = { workspace = true } pallet-balances = { workspace = true, features = ["std"] } - +pallet-scheduler = { workspace = true } +sp-std = { workspace = true } [features] default = ["std"] @@ -50,12 +51,14 @@ std = [ "pallet-subtensor/std", "sp-consensus-aura/std", "pallet-balances/std", + "pallet-scheduler/std", "sp-runtime/std", "sp-tracing/std", "sp-weights/std", "log/std", "sp-core/std", "sp-io/std", + "sp-std/std", "substrate-fixed/std", ] runtime-benchmarks = [ @@ -64,12 +67,14 @@ runtime-benchmarks = [ "frame-system/runtime-benchmarks", "pallet-balances/runtime-benchmarks", "sp-runtime/runtime-benchmarks", - "pallet-subtensor/runtime-benchmarks" + "pallet-subtensor/runtime-benchmarks", + "pallet-scheduler/runtime-benchmarks", ] try-runtime = [ "frame-support/try-runtime", "frame-system/try-runtime", "pallet-balances/try-runtime", + "pallet-scheduler/try-runtime", "sp-runtime/try-runtime", "pallet-subtensor/try-runtime" ] diff --git a/pallets/admin-utils/src/benchmarking.rs b/pallets/admin-utils/src/benchmarking.rs index 33e026553..7515525f0 100644 --- a/pallets/admin-utils/src/benchmarking.rs +++ b/pallets/admin-utils/src/benchmarking.rs @@ -47,7 +47,7 @@ mod benchmarks { #[benchmark] fn sudo_set_max_difficulty() { - T::Subtensor::init_new_network(1u16 /*netuid*/, 1u16 /*tempo*/); + pallet_subtensor::Pallet::::init_new_network(1u16 /*netuid*/, 1u16 /*tempo*/); #[extrinsic_call] _(RawOrigin::Root, 1u16/*netuid*/, 10000u64/*max_difficulty*/)/*sudo_set_max_difficulty*/; @@ -55,7 +55,7 @@ mod benchmarks { #[benchmark] fn sudo_set_min_difficulty() { - T::Subtensor::init_new_network(1u16 /*netuid*/, 1u16 /*tempo*/); + pallet_subtensor::Pallet::::init_new_network(1u16 /*netuid*/, 1u16 /*tempo*/); #[extrinsic_call] _(RawOrigin::Root, 1u16/*netuid*/, 1000u64/*min_difficulty*/)/*sudo_set_min_difficulty*/; @@ -63,7 +63,7 @@ mod benchmarks { #[benchmark] fn sudo_set_weights_set_rate_limit() { - T::Subtensor::init_new_network(1u16 /*netuid*/, 1u16 /*tempo*/); + pallet_subtensor::Pallet::::init_new_network(1u16 /*netuid*/, 1u16 /*tempo*/); #[extrinsic_call] _(RawOrigin::Root, 1u16/*netuid*/, 3u64/*rate_limit*/)/*sudo_set_weights_set_rate_limit*/; @@ -71,7 +71,7 @@ mod benchmarks { #[benchmark] fn sudo_set_weights_version_key() { - T::Subtensor::init_new_network(1u16 /*netuid*/, 1u16 /*tempo*/); + pallet_subtensor::Pallet::::init_new_network(1u16 /*netuid*/, 1u16 /*tempo*/); #[extrinsic_call] _(RawOrigin::Root, 1u16/*netuid*/, 1u64/*version_key*/)/*sudo_set_weights_version_key*/; @@ -79,7 +79,7 @@ mod benchmarks { #[benchmark] fn sudo_set_bonds_moving_average() { - T::Subtensor::init_new_network(1u16 /*netuid*/, 1u16 /*tempo*/); + pallet_subtensor::Pallet::::init_new_network(1u16 /*netuid*/, 1u16 /*tempo*/); #[extrinsic_call] _(RawOrigin::Root, 1u16/*netuid*/, 100u64/*bonds_moving_average*/)/*sudo_set_bonds_moving_average*/; @@ -87,7 +87,7 @@ mod benchmarks { #[benchmark] fn sudo_set_max_allowed_validators() { - T::Subtensor::init_new_network(1u16 /*netuid*/, 1u16 /*tempo*/); + pallet_subtensor::Pallet::::init_new_network(1u16 /*netuid*/, 1u16 /*tempo*/); #[extrinsic_call] _(RawOrigin::Root, 1u16/*netuid*/, 10u16/*max_allowed_validators*/)/*sudo_set_max_allowed_validators*/; @@ -95,7 +95,7 @@ mod benchmarks { #[benchmark] fn sudo_set_difficulty() { - T::Subtensor::init_new_network(1u16 /*netuid*/, 1u16 /*tempo*/); + pallet_subtensor::Pallet::::init_new_network(1u16 /*netuid*/, 1u16 /*tempo*/); #[extrinsic_call] _(RawOrigin::Root, 1u16/*netuid*/, 1200000u64/*difficulty*/)/*sudo_set_difficulty*/; @@ -103,7 +103,7 @@ mod benchmarks { #[benchmark] fn sudo_set_adjustment_interval() { - T::Subtensor::init_new_network(1u16 /*netuid*/, 1u16 /*tempo*/); + pallet_subtensor::Pallet::::init_new_network(1u16 /*netuid*/, 1u16 /*tempo*/); #[extrinsic_call] _(RawOrigin::Root, 1u16/*netuid*/, 12u16/*adjustment_interval*/)/*sudo_set_adjustment_interval*/; @@ -111,7 +111,7 @@ mod benchmarks { #[benchmark] fn sudo_set_target_registrations_per_interval() { - T::Subtensor::init_new_network(1u16 /*netuid*/, 1u16 /*tempo*/); + pallet_subtensor::Pallet::::init_new_network(1u16 /*netuid*/, 1u16 /*tempo*/); #[extrinsic_call] _(RawOrigin::Root, 1u16/*netuid*/, 300u16/*target_registrations*/)/*sudo_set_target_registrations_per_interval*/; @@ -119,7 +119,7 @@ mod benchmarks { #[benchmark] fn sudo_set_activity_cutoff() { - T::Subtensor::init_new_network(1u16 /*netuid*/, 1u16 /*tempo*/); + pallet_subtensor::Pallet::::init_new_network(1u16 /*netuid*/, 1u16 /*tempo*/); #[extrinsic_call] _(RawOrigin::Root, 1u16/*netuid*/, 300u16/*activity_cutoff*/)/*sudo_set_activity_cutoff*/; @@ -127,7 +127,7 @@ mod benchmarks { #[benchmark] fn sudo_set_rho() { - T::Subtensor::init_new_network(1u16 /*netuid*/, 1u16 /*tempo*/); + pallet_subtensor::Pallet::::init_new_network(1u16 /*netuid*/, 1u16 /*tempo*/); #[extrinsic_call] _(RawOrigin::Root, 1u16/*netuid*/, 300u16/*rho*/)/*sudo_set_rho*/; @@ -135,7 +135,10 @@ mod benchmarks { #[benchmark] fn sudo_set_kappa() { - T::Subtensor::init_new_network(1u16 /*netuid*/, 1u16 /*sudo_tempo*/); + pallet_subtensor::Pallet::::init_new_network( + 1u16, /*netuid*/ + 1u16, /*sudo_tempo*/ + ); #[extrinsic_call] _(RawOrigin::Root, 1u16/*netuid*/, 3u16/*kappa*/)/*set_kappa*/; @@ -143,7 +146,7 @@ mod benchmarks { #[benchmark] fn sudo_set_max_allowed_uids() { - T::Subtensor::init_new_network(1u16 /*netuid*/, 1u16 /*tempo*/); + pallet_subtensor::Pallet::::init_new_network(1u16 /*netuid*/, 1u16 /*tempo*/); #[extrinsic_call] _(RawOrigin::Root, 1u16/*netuid*/, 4097u16/*max_allowed_uids*/)/*sudo_set_max_allowed_uids*/; @@ -151,7 +154,7 @@ mod benchmarks { #[benchmark] fn sudo_set_min_allowed_weights() { - T::Subtensor::init_new_network(1u16 /*netuid*/, 1u16 /*tempo*/); + pallet_subtensor::Pallet::::init_new_network(1u16 /*netuid*/, 1u16 /*tempo*/); #[extrinsic_call] _(RawOrigin::Root, 1u16/*netuid*/, 10u16/*max_allowed_uids*/)/*sudo_set_min_allowed_weights*/; @@ -159,7 +162,7 @@ mod benchmarks { #[benchmark] fn sudo_set_immunity_period() { - T::Subtensor::init_new_network(1u16 /*netuid*/, 1u16 /*tempo*/); + pallet_subtensor::Pallet::::init_new_network(1u16 /*netuid*/, 1u16 /*tempo*/); #[extrinsic_call] _(RawOrigin::Root, 1u16/*netuid*/, 100u16/*immunity_period*/)/*sudo_set_immunity_period*/; @@ -167,7 +170,7 @@ mod benchmarks { #[benchmark] fn sudo_set_max_weight_limit() { - T::Subtensor::init_new_network(1u16 /*netuid*/, 1u16 /*tempo*/); + pallet_subtensor::Pallet::::init_new_network(1u16 /*netuid*/, 1u16 /*tempo*/); #[extrinsic_call] _(RawOrigin::Root, 1u16/*netuid*/, 100u16/*max_weight_limit*/)/*sudo_set_max_weight_limit*/; @@ -175,7 +178,7 @@ mod benchmarks { #[benchmark] fn sudo_set_max_registrations_per_block() { - T::Subtensor::init_new_network(1u16 /*netuid*/, 1u16 /*tempo*/); + pallet_subtensor::Pallet::::init_new_network(1u16 /*netuid*/, 1u16 /*tempo*/); #[extrinsic_call] _(RawOrigin::Root, 1u16/*netuid*/, 100u16/*max_registrations*/)/*sudo_set_max_registrations_per_block*/; @@ -183,7 +186,7 @@ mod benchmarks { #[benchmark] fn sudo_set_max_burn() { - T::Subtensor::init_new_network(1u16 /*netuid*/, 1u16 /*tempo*/); + pallet_subtensor::Pallet::::init_new_network(1u16 /*netuid*/, 1u16 /*tempo*/); #[extrinsic_call] _(RawOrigin::Root, 1u16/*netuid*/, 10u64/*max_burn*/)/*sudo_set_max_burn*/; @@ -191,7 +194,7 @@ mod benchmarks { #[benchmark] fn sudo_set_min_burn() { - T::Subtensor::init_new_network(1u16 /*netuid*/, 1u16 /*tempo*/); + pallet_subtensor::Pallet::::init_new_network(1u16 /*netuid*/, 1u16 /*tempo*/); #[extrinsic_call] _(RawOrigin::Root, 1u16/*netuid*/, 10u64/*min_burn*/)/*sudo_set_min_burn*/; @@ -199,7 +202,7 @@ mod benchmarks { #[benchmark] fn sudo_set_network_registration_allowed() { - T::Subtensor::init_new_network(1u16 /*netuid*/, 1u16 /*tempo*/); + pallet_subtensor::Pallet::::init_new_network(1u16 /*netuid*/, 1u16 /*tempo*/); #[extrinsic_call] _(RawOrigin::Root, 1u16/*netuid*/, true/*registration_allowed*/)/*sudo_set_network_registration_allowed*/; @@ -212,13 +215,13 @@ mod benchmarks { let tempo: u16 = 15; let modality: u16 = 0; - T::Subtensor::init_new_network(netuid, tempo); + pallet_subtensor::Pallet::::init_new_network(netuid, tempo); }: sudo_set_tempo(RawOrigin::>::Root, netuid, tempo) */ #[benchmark] fn sudo_set_tempo() { - T::Subtensor::init_new_network(1u16 /*netuid*/, 1u16 /*tempo*/); + pallet_subtensor::Pallet::::init_new_network(1u16 /*netuid*/, 1u16 /*tempo*/); #[extrinsic_call] _(RawOrigin::Root, 1u16/*netuid*/, 1u16/*tempo*/)/*sudo_set_tempo*/; @@ -226,7 +229,10 @@ mod benchmarks { #[benchmark] fn sudo_set_commit_reveal_weights_interval() { - T::Subtensor::init_new_network(1u16 /*netuid*/, 1u16 /*sudo_tempo*/); + pallet_subtensor::Pallet::::init_new_network( + 1u16, /*netuid*/ + 1u16, /*sudo_tempo*/ + ); #[extrinsic_call] _(RawOrigin::Root, 1u16/*netuid*/, 3u64/*interval*/)/*set_commit_reveal_weights_interval()*/; @@ -234,7 +240,10 @@ mod benchmarks { #[benchmark] fn sudo_set_commit_reveal_weights_enabled() { - T::Subtensor::init_new_network(1u16 /*netuid*/, 1u16 /*sudo_tempo*/); + pallet_subtensor::Pallet::::init_new_network( + 1u16, /*netuid*/ + 1u16, /*sudo_tempo*/ + ); #[extrinsic_call] _(RawOrigin::Root, 1u16/*netuid*/, true/*enabled*/)/*set_commit_reveal_weights_enabled*/; @@ -242,7 +251,10 @@ mod benchmarks { #[benchmark] fn sudo_set_hotkey_emission_tempo() { - T::Subtensor::init_new_network(1u16 /*netuid*/, 1u16 /*sudo_tempo*/); + pallet_subtensor::Pallet::::init_new_network( + 1u16, /*netuid*/ + 1u16, /*sudo_tempo*/ + ); #[extrinsic_call] _(RawOrigin::Root, 1u64/*emission_tempo*/)/*set_hotkey_emission_tempo*/; @@ -250,7 +262,7 @@ mod benchmarks { #[benchmark] fn sudo_set_network_max_stake() { - T::Subtensor::init_new_network(1u16 /*netuid*/, 1u16 /*tempo*/); + pallet_subtensor::Pallet::::init_new_network(1u16 /*netuid*/, 1u16 /*tempo*/); #[extrinsic_call] _(RawOrigin::Root, 1u16/*netuid*/, 1_000_000_000_000_000u64/*max_stake*/)/*sudo_set_network_max_stake*/; diff --git a/pallets/admin-utils/src/lib.rs b/pallets/admin-utils/src/lib.rs index 59b0ed44a..baf711b85 100644 --- a/pallets/admin-utils/src/lib.rs +++ b/pallets/admin-utils/src/lib.rs @@ -4,7 +4,6 @@ pub use pallet::*; pub mod weights; pub use weights::WeightInfo; -use sp_runtime::DispatchError; use sp_runtime::{traits::Member, RuntimeAppPublic}; mod benchmarking; @@ -26,7 +25,7 @@ pub mod pallet { /// Configure the pallet by specifying the parameters and types on which it depends. #[pallet::config] - pub trait Config: frame_system::Config { + pub trait Config: frame_system::Config + pallet_subtensor::pallet::Config { /// Because this pallet emits events, it depends on the runtime's definition of an event. type RuntimeEvent: From> + IsType<::RuntimeEvent>; @@ -48,13 +47,6 @@ pub mod pallet { /// Unit of assets type Balance: Balance; - - /// Implementation of the subtensor interface - type Subtensor: crate::SubtensorInterface< - Self::AccountId, - Self::Balance, - Self::RuntimeOrigin, - >; } #[pallet::event] @@ -87,7 +79,7 @@ pub mod pallet { T::Aura::change_authorities(new_authorities.clone()); - log::info!("Aura authorities changed: {:?}", new_authorities); + log::debug!("Aura authorities changed: {:?}", new_authorities); // Return a successful DispatchResultWithPostInfo Ok(()) @@ -100,8 +92,8 @@ pub mod pallet { #[pallet::weight(T::WeightInfo::sudo_set_default_take())] pub fn sudo_set_default_take(origin: OriginFor, default_take: u16) -> DispatchResult { ensure_root(origin)?; - T::Subtensor::set_max_delegate_take(default_take); - log::info!("DefaultTakeSet( default_take: {:?} ) ", default_take); + pallet_subtensor::Pallet::::set_max_delegate_take(default_take); + log::debug!("DefaultTakeSet( default_take: {:?} ) ", default_take); Ok(()) } @@ -112,8 +104,8 @@ pub mod pallet { #[pallet::weight((0, DispatchClass::Operational, Pays::No))] pub fn sudo_set_tx_rate_limit(origin: OriginFor, tx_rate_limit: u64) -> DispatchResult { ensure_root(origin)?; - T::Subtensor::set_tx_rate_limit(tx_rate_limit); - log::info!("TxRateLimitSet( tx_rate_limit: {:?} ) ", tx_rate_limit); + pallet_subtensor::Pallet::::set_tx_rate_limit(tx_rate_limit); + log::debug!("TxRateLimitSet( tx_rate_limit: {:?} ) ", tx_rate_limit); Ok(()) } @@ -127,10 +119,10 @@ pub mod pallet { netuid: u16, serving_rate_limit: u64, ) -> DispatchResult { - T::Subtensor::ensure_subnet_owner_or_root(origin, netuid)?; + pallet_subtensor::Pallet::::ensure_subnet_owner_or_root(origin, netuid)?; - T::Subtensor::set_serving_rate_limit(netuid, serving_rate_limit); - log::info!( + pallet_subtensor::Pallet::::set_serving_rate_limit(netuid, serving_rate_limit); + log::debug!( "ServingRateLimitSet( serving_rate_limit: {:?} ) ", serving_rate_limit ); @@ -147,14 +139,14 @@ pub mod pallet { netuid: u16, min_difficulty: u64, ) -> DispatchResult { - T::Subtensor::ensure_subnet_owner_or_root(origin, netuid)?; + pallet_subtensor::Pallet::::ensure_subnet_owner_or_root(origin, netuid)?; ensure!( - T::Subtensor::if_subnet_exist(netuid), + pallet_subtensor::Pallet::::if_subnet_exist(netuid), Error::::SubnetDoesNotExist ); - T::Subtensor::set_min_difficulty(netuid, min_difficulty); - log::info!( + pallet_subtensor::Pallet::::set_min_difficulty(netuid, min_difficulty); + log::debug!( "MinDifficultySet( netuid: {:?} min_difficulty: {:?} ) ", netuid, min_difficulty @@ -172,14 +164,14 @@ pub mod pallet { netuid: u16, max_difficulty: u64, ) -> DispatchResult { - T::Subtensor::ensure_subnet_owner_or_root(origin, netuid)?; + pallet_subtensor::Pallet::::ensure_subnet_owner_or_root(origin, netuid)?; ensure!( - T::Subtensor::if_subnet_exist(netuid), + pallet_subtensor::Pallet::::if_subnet_exist(netuid), Error::::SubnetDoesNotExist ); - T::Subtensor::set_max_difficulty(netuid, max_difficulty); - log::info!( + pallet_subtensor::Pallet::::set_max_difficulty(netuid, max_difficulty); + log::debug!( "MaxDifficultySet( netuid: {:?} max_difficulty: {:?} ) ", netuid, max_difficulty @@ -197,14 +189,14 @@ pub mod pallet { netuid: u16, weights_version_key: u64, ) -> DispatchResult { - T::Subtensor::ensure_subnet_owner_or_root(origin, netuid)?; + pallet_subtensor::Pallet::::ensure_subnet_owner_or_root(origin, netuid)?; ensure!( - T::Subtensor::if_subnet_exist(netuid), + pallet_subtensor::Pallet::::if_subnet_exist(netuid), Error::::SubnetDoesNotExist ); - T::Subtensor::set_weights_version_key(netuid, weights_version_key); - log::info!( + pallet_subtensor::Pallet::::set_weights_version_key(netuid, weights_version_key); + log::debug!( "WeightsVersionKeySet( netuid: {:?} weights_version_key: {:?} ) ", netuid, weights_version_key @@ -222,14 +214,17 @@ pub mod pallet { netuid: u16, weights_set_rate_limit: u64, ) -> DispatchResult { - T::Subtensor::ensure_subnet_owner_or_root(origin, netuid)?; + pallet_subtensor::Pallet::::ensure_subnet_owner_or_root(origin, netuid)?; ensure!( - T::Subtensor::if_subnet_exist(netuid), + pallet_subtensor::Pallet::::if_subnet_exist(netuid), Error::::SubnetDoesNotExist ); - T::Subtensor::set_weights_set_rate_limit(netuid, weights_set_rate_limit); - log::info!( + pallet_subtensor::Pallet::::set_weights_set_rate_limit( + netuid, + weights_set_rate_limit, + ); + log::debug!( "WeightsSetRateLimitSet( netuid: {:?} weights_set_rate_limit: {:?} ) ", netuid, weights_set_rate_limit @@ -250,11 +245,11 @@ pub mod pallet { ensure_root(origin)?; ensure!( - T::Subtensor::if_subnet_exist(netuid), + pallet_subtensor::Pallet::::if_subnet_exist(netuid), Error::::SubnetDoesNotExist ); - T::Subtensor::set_adjustment_interval(netuid, adjustment_interval); - log::info!( + pallet_subtensor::Pallet::::set_adjustment_interval(netuid, adjustment_interval); + log::debug!( "AdjustmentIntervalSet( netuid: {:?} adjustment_interval: {:?} ) ", netuid, adjustment_interval @@ -278,14 +273,14 @@ pub mod pallet { netuid: u16, adjustment_alpha: u64, ) -> DispatchResult { - T::Subtensor::ensure_subnet_owner_or_root(origin, netuid)?; + pallet_subtensor::Pallet::::ensure_subnet_owner_or_root(origin, netuid)?; ensure!( - T::Subtensor::if_subnet_exist(netuid), + pallet_subtensor::Pallet::::if_subnet_exist(netuid), Error::::SubnetDoesNotExist ); - T::Subtensor::set_adjustment_alpha(netuid, adjustment_alpha); - log::info!( + pallet_subtensor::Pallet::::set_adjustment_alpha(netuid, adjustment_alpha); + log::debug!( "AdjustmentAlphaSet( adjustment_alpha: {:?} ) ", adjustment_alpha ); @@ -302,14 +297,14 @@ pub mod pallet { netuid: u16, max_weight_limit: u16, ) -> DispatchResult { - T::Subtensor::ensure_subnet_owner_or_root(origin, netuid)?; + pallet_subtensor::Pallet::::ensure_subnet_owner_or_root(origin, netuid)?; ensure!( - T::Subtensor::if_subnet_exist(netuid), + pallet_subtensor::Pallet::::if_subnet_exist(netuid), Error::::SubnetDoesNotExist ); - T::Subtensor::set_max_weight_limit(netuid, max_weight_limit); - log::info!( + pallet_subtensor::Pallet::::set_max_weight_limit(netuid, max_weight_limit); + log::debug!( "MaxWeightLimitSet( netuid: {:?} max_weight_limit: {:?} ) ", netuid, max_weight_limit @@ -327,14 +322,14 @@ pub mod pallet { netuid: u16, immunity_period: u16, ) -> DispatchResult { - T::Subtensor::ensure_subnet_owner_or_root(origin, netuid)?; + pallet_subtensor::Pallet::::ensure_subnet_owner_or_root(origin, netuid)?; ensure!( - T::Subtensor::if_subnet_exist(netuid), + pallet_subtensor::Pallet::::if_subnet_exist(netuid), Error::::SubnetDoesNotExist ); - T::Subtensor::set_immunity_period(netuid, immunity_period); - log::info!( + pallet_subtensor::Pallet::::set_immunity_period(netuid, immunity_period); + log::debug!( "ImmunityPeriodSet( netuid: {:?} immunity_period: {:?} ) ", netuid, immunity_period @@ -352,14 +347,14 @@ pub mod pallet { netuid: u16, min_allowed_weights: u16, ) -> DispatchResult { - T::Subtensor::ensure_subnet_owner_or_root(origin, netuid)?; + pallet_subtensor::Pallet::::ensure_subnet_owner_or_root(origin, netuid)?; ensure!( - T::Subtensor::if_subnet_exist(netuid), + pallet_subtensor::Pallet::::if_subnet_exist(netuid), Error::::SubnetDoesNotExist ); - T::Subtensor::set_min_allowed_weights(netuid, min_allowed_weights); - log::info!( + pallet_subtensor::Pallet::::set_min_allowed_weights(netuid, min_allowed_weights); + log::debug!( "MinAllowedWeightSet( netuid: {:?} min_allowed_weights: {:?} ) ", netuid, min_allowed_weights @@ -379,15 +374,15 @@ pub mod pallet { ) -> DispatchResult { ensure_root(origin)?; ensure!( - T::Subtensor::if_subnet_exist(netuid), + pallet_subtensor::Pallet::::if_subnet_exist(netuid), Error::::SubnetDoesNotExist ); ensure!( - T::Subtensor::get_subnetwork_n(netuid) < max_allowed_uids, + pallet_subtensor::Pallet::::get_subnetwork_n(netuid) < max_allowed_uids, Error::::MaxAllowedUIdsLessThanCurrentUIds ); - T::Subtensor::set_max_allowed_uids(netuid, max_allowed_uids); - log::info!( + pallet_subtensor::Pallet::::set_max_allowed_uids(netuid, max_allowed_uids); + log::debug!( "MaxAllowedUidsSet( netuid: {:?} max_allowed_uids: {:?} ) ", netuid, max_allowed_uids @@ -401,14 +396,14 @@ pub mod pallet { #[pallet::call_index(16)] #[pallet::weight(T::WeightInfo::sudo_set_kappa())] pub fn sudo_set_kappa(origin: OriginFor, netuid: u16, kappa: u16) -> DispatchResult { - T::Subtensor::ensure_subnet_owner_or_root(origin, netuid)?; + pallet_subtensor::Pallet::::ensure_subnet_owner_or_root(origin, netuid)?; ensure!( - T::Subtensor::if_subnet_exist(netuid), + pallet_subtensor::Pallet::::if_subnet_exist(netuid), Error::::SubnetDoesNotExist ); - T::Subtensor::set_kappa(netuid, kappa); - log::info!("KappaSet( netuid: {:?} kappa: {:?} ) ", netuid, kappa); + pallet_subtensor::Pallet::::set_kappa(netuid, kappa); + log::debug!("KappaSet( netuid: {:?} kappa: {:?} ) ", netuid, kappa); Ok(()) } @@ -418,14 +413,14 @@ pub mod pallet { #[pallet::call_index(17)] #[pallet::weight(T::WeightInfo::sudo_set_rho())] pub fn sudo_set_rho(origin: OriginFor, netuid: u16, rho: u16) -> DispatchResult { - T::Subtensor::ensure_subnet_owner_or_root(origin, netuid)?; + pallet_subtensor::Pallet::::ensure_subnet_owner_or_root(origin, netuid)?; ensure!( - T::Subtensor::if_subnet_exist(netuid), + pallet_subtensor::Pallet::::if_subnet_exist(netuid), Error::::SubnetDoesNotExist ); - T::Subtensor::set_rho(netuid, rho); - log::info!("RhoSet( netuid: {:?} rho: {:?} ) ", netuid, rho); + pallet_subtensor::Pallet::::set_rho(netuid, rho); + log::debug!("RhoSet( netuid: {:?} rho: {:?} ) ", netuid, rho); Ok(()) } @@ -439,14 +434,14 @@ pub mod pallet { netuid: u16, activity_cutoff: u16, ) -> DispatchResult { - T::Subtensor::ensure_subnet_owner_or_root(origin, netuid)?; + pallet_subtensor::Pallet::::ensure_subnet_owner_or_root(origin, netuid)?; ensure!( - T::Subtensor::if_subnet_exist(netuid), + pallet_subtensor::Pallet::::if_subnet_exist(netuid), Error::::SubnetDoesNotExist ); - T::Subtensor::set_activity_cutoff(netuid, activity_cutoff); - log::info!( + pallet_subtensor::Pallet::::set_activity_cutoff(netuid, activity_cutoff); + log::debug!( "ActivityCutoffSet( netuid: {:?} activity_cutoff: {:?} ) ", netuid, activity_cutoff @@ -470,10 +465,13 @@ pub mod pallet { netuid: u16, registration_allowed: bool, ) -> DispatchResult { - T::Subtensor::ensure_subnet_owner_or_root(origin, netuid)?; + pallet_subtensor::Pallet::::ensure_subnet_owner_or_root(origin, netuid)?; - T::Subtensor::set_network_registration_allowed(netuid, registration_allowed); - log::info!( + pallet_subtensor::Pallet::::set_network_registration_allowed( + netuid, + registration_allowed, + ); + log::debug!( "NetworkRegistrationAllowed( registration_allowed: {:?} ) ", registration_allowed ); @@ -495,10 +493,13 @@ pub mod pallet { netuid: u16, registration_allowed: bool, ) -> DispatchResult { - T::Subtensor::ensure_subnet_owner_or_root(origin, netuid)?; + pallet_subtensor::Pallet::::ensure_subnet_owner_or_root(origin, netuid)?; - T::Subtensor::set_network_pow_registration_allowed(netuid, registration_allowed); - log::info!( + pallet_subtensor::Pallet::::set_network_pow_registration_allowed( + netuid, + registration_allowed, + ); + log::debug!( "NetworkPowRegistrationAllowed( registration_allowed: {:?} ) ", registration_allowed ); @@ -518,14 +519,14 @@ pub mod pallet { ensure_root(origin)?; ensure!( - T::Subtensor::if_subnet_exist(netuid), + pallet_subtensor::Pallet::::if_subnet_exist(netuid), Error::::SubnetDoesNotExist ); - T::Subtensor::set_target_registrations_per_interval( + pallet_subtensor::Pallet::::set_target_registrations_per_interval( netuid, target_registrations_per_interval, ); - log::info!( + log::debug!( "RegistrationPerIntervalSet( netuid: {:?} target_registrations_per_interval: {:?} ) ", netuid, target_registrations_per_interval @@ -543,14 +544,14 @@ pub mod pallet { netuid: u16, min_burn: u64, ) -> DispatchResult { - T::Subtensor::ensure_subnet_owner_or_root(origin, netuid)?; + pallet_subtensor::Pallet::::ensure_subnet_owner_or_root(origin, netuid)?; ensure!( - T::Subtensor::if_subnet_exist(netuid), + pallet_subtensor::Pallet::::if_subnet_exist(netuid), Error::::SubnetDoesNotExist ); - T::Subtensor::set_min_burn(netuid, min_burn); - log::info!( + pallet_subtensor::Pallet::::set_min_burn(netuid, min_burn); + log::debug!( "MinBurnSet( netuid: {:?} min_burn: {:?} ) ", netuid, min_burn @@ -568,14 +569,14 @@ pub mod pallet { netuid: u16, max_burn: u64, ) -> DispatchResult { - T::Subtensor::ensure_subnet_owner_or_root(origin, netuid)?; + pallet_subtensor::Pallet::::ensure_subnet_owner_or_root(origin, netuid)?; ensure!( - T::Subtensor::if_subnet_exist(netuid), + pallet_subtensor::Pallet::::if_subnet_exist(netuid), Error::::SubnetDoesNotExist ); - T::Subtensor::set_max_burn(netuid, max_burn); - log::info!( + pallet_subtensor::Pallet::::set_max_burn(netuid, max_burn); + log::debug!( "MaxBurnSet( netuid: {:?} max_burn: {:?} ) ", netuid, max_burn @@ -593,13 +594,13 @@ pub mod pallet { netuid: u16, difficulty: u64, ) -> DispatchResult { - T::Subtensor::ensure_subnet_owner_or_root(origin, netuid)?; + pallet_subtensor::Pallet::::ensure_subnet_owner_or_root(origin, netuid)?; ensure!( - T::Subtensor::if_subnet_exist(netuid), + pallet_subtensor::Pallet::::if_subnet_exist(netuid), Error::::SubnetDoesNotExist ); - T::Subtensor::set_difficulty(netuid, difficulty); - log::info!( + pallet_subtensor::Pallet::::set_difficulty(netuid, difficulty); + log::debug!( "DifficultySet( netuid: {:?} difficulty: {:?} ) ", netuid, difficulty @@ -619,16 +620,20 @@ pub mod pallet { ) -> DispatchResult { ensure_root(origin)?; ensure!( - T::Subtensor::if_subnet_exist(netuid), + pallet_subtensor::Pallet::::if_subnet_exist(netuid), Error::::SubnetDoesNotExist ); ensure!( - max_allowed_validators <= T::Subtensor::get_max_allowed_uids(netuid), + max_allowed_validators + <= pallet_subtensor::Pallet::::get_max_allowed_uids(netuid), Error::::MaxValidatorsLargerThanMaxUIds ); - T::Subtensor::set_max_allowed_validators(netuid, max_allowed_validators); - log::info!( + pallet_subtensor::Pallet::::set_max_allowed_validators( + netuid, + max_allowed_validators, + ); + log::debug!( "MaxAllowedValidatorsSet( netuid: {:?} max_allowed_validators: {:?} ) ", netuid, max_allowed_validators @@ -646,14 +651,14 @@ pub mod pallet { netuid: u16, bonds_moving_average: u64, ) -> DispatchResult { - T::Subtensor::ensure_subnet_owner_or_root(origin, netuid)?; + pallet_subtensor::Pallet::::ensure_subnet_owner_or_root(origin, netuid)?; ensure!( - T::Subtensor::if_subnet_exist(netuid), + pallet_subtensor::Pallet::::if_subnet_exist(netuid), Error::::SubnetDoesNotExist ); - T::Subtensor::set_bonds_moving_average(netuid, bonds_moving_average); - log::info!( + pallet_subtensor::Pallet::::set_bonds_moving_average(netuid, bonds_moving_average); + log::debug!( "BondsMovingAverageSet( netuid: {:?} bonds_moving_average: {:?} ) ", netuid, bonds_moving_average @@ -674,11 +679,14 @@ pub mod pallet { ensure_root(origin)?; ensure!( - T::Subtensor::if_subnet_exist(netuid), + pallet_subtensor::Pallet::::if_subnet_exist(netuid), Error::::SubnetDoesNotExist ); - T::Subtensor::set_max_registrations_per_block(netuid, max_registrations_per_block); - log::info!( + pallet_subtensor::Pallet::::set_max_registrations_per_block( + netuid, + max_registrations_per_block, + ); + log::debug!( "MaxRegistrationsPerBlock( netuid: {:?} max_registrations_per_block: {:?} ) ", netuid, max_registrations_per_block @@ -701,8 +709,8 @@ pub mod pallet { subnet_owner_cut: u16, ) -> DispatchResult { ensure_root(origin)?; - T::Subtensor::set_subnet_owner_cut(subnet_owner_cut); - log::info!( + pallet_subtensor::Pallet::::set_subnet_owner_cut(subnet_owner_cut); + log::debug!( "SubnetOwnerCut( subnet_owner_cut: {:?} ) ", subnet_owner_cut ); @@ -724,8 +732,8 @@ pub mod pallet { rate_limit: u64, ) -> DispatchResult { ensure_root(origin)?; - T::Subtensor::set_network_rate_limit(rate_limit); - log::info!("NetworkRateLimit( rate_limit: {:?} ) ", rate_limit); + pallet_subtensor::Pallet::::set_network_rate_limit(rate_limit); + log::debug!("NetworkRateLimit( rate_limit: {:?} ) ", rate_limit); Ok(()) } @@ -737,11 +745,11 @@ pub mod pallet { pub fn sudo_set_tempo(origin: OriginFor, netuid: u16, tempo: u16) -> DispatchResult { ensure_root(origin)?; ensure!( - T::Subtensor::if_subnet_exist(netuid), + pallet_subtensor::Pallet::::if_subnet_exist(netuid), Error::::SubnetDoesNotExist ); - T::Subtensor::set_tempo(netuid, tempo); - log::info!("TempoSet( netuid: {:?} tempo: {:?} ) ", netuid, tempo); + pallet_subtensor::Pallet::::set_tempo(netuid, tempo); + log::debug!("TempoSet( netuid: {:?} tempo: {:?} ) ", netuid, tempo); Ok(()) } @@ -756,7 +764,7 @@ pub mod pallet { ) -> DispatchResult { ensure_root(origin)?; - T::Subtensor::set_total_issuance(total_issuance); + pallet_subtensor::Pallet::::set_total_issuance(total_issuance); Ok(()) } @@ -777,9 +785,9 @@ pub mod pallet { ) -> DispatchResult { ensure_root(origin)?; - T::Subtensor::set_network_immunity_period(immunity_period); + pallet_subtensor::Pallet::::set_network_immunity_period(immunity_period); - log::info!("NetworkImmunityPeriod( period: {:?} ) ", immunity_period); + log::debug!("NetworkImmunityPeriod( period: {:?} ) ", immunity_period); Ok(()) } @@ -800,9 +808,9 @@ pub mod pallet { ) -> DispatchResult { ensure_root(origin)?; - T::Subtensor::set_network_min_lock(lock_cost); + pallet_subtensor::Pallet::::set_network_min_lock(lock_cost); - log::info!("NetworkMinLockCost( lock_cost: {:?} ) ", lock_cost); + log::debug!("NetworkMinLockCost( lock_cost: {:?} ) ", lock_cost); Ok(()) } @@ -819,9 +827,9 @@ pub mod pallet { ))] pub fn sudo_set_subnet_limit(origin: OriginFor, max_subnets: u16) -> DispatchResult { ensure_root(origin)?; - T::Subtensor::set_subnet_limit(max_subnets); + pallet_subtensor::Pallet::::set_max_subnets(max_subnets); - log::info!("SubnetLimit( max_subnets: {:?} ) ", max_subnets); + log::debug!("SubnetLimit( max_subnets: {:?} ) ", max_subnets); Ok(()) } @@ -842,9 +850,9 @@ pub mod pallet { ) -> DispatchResult { ensure_root(origin)?; - T::Subtensor::set_lock_reduction_interval(interval); + pallet_subtensor::Pallet::::set_lock_reduction_interval(interval); - log::info!("NetworkLockReductionInterval( interval: {:?} ) ", interval); + log::debug!("NetworkLockReductionInterval( interval: {:?} ) ", interval); Ok(()) } @@ -861,10 +869,10 @@ pub mod pallet { ) -> DispatchResult { ensure_root(origin)?; ensure!( - T::Subtensor::if_subnet_exist(netuid), + pallet_subtensor::Pallet::::if_subnet_exist(netuid), Error::::SubnetDoesNotExist ); - T::Subtensor::set_rao_recycled(netuid, rao_recycled); + pallet_subtensor::Pallet::::set_rao_recycled(netuid, rao_recycled); Ok(()) } @@ -875,7 +883,7 @@ pub mod pallet { #[pallet::weight((0, DispatchClass::Operational, Pays::No))] pub fn sudo_set_weights_min_stake(origin: OriginFor, min_stake: u64) -> DispatchResult { ensure_root(origin)?; - T::Subtensor::set_weights_min_stake(min_stake); + pallet_subtensor::Pallet::::set_weights_min_stake(min_stake); Ok(()) } @@ -890,12 +898,12 @@ pub mod pallet { min_stake: u64, ) -> DispatchResult { ensure_root(origin)?; - let prev_min_stake = T::Subtensor::get_nominator_min_required_stake(); + let prev_min_stake = pallet_subtensor::Pallet::::get_nominator_min_required_stake(); log::trace!("Setting minimum stake to: {}", min_stake); - T::Subtensor::set_nominator_min_required_stake(min_stake); + pallet_subtensor::Pallet::::set_nominator_min_required_stake(min_stake); if min_stake > prev_min_stake { log::trace!("Clearing small nominations"); - T::Subtensor::clear_small_nominations(); + pallet_subtensor::Pallet::::clear_small_nominations(); log::trace!("Small nominations cleared"); } Ok(()) @@ -911,8 +919,8 @@ pub mod pallet { tx_rate_limit: u64, ) -> DispatchResult { ensure_root(origin)?; - T::Subtensor::set_tx_delegate_take_rate_limit(tx_rate_limit); - log::info!( + pallet_subtensor::Pallet::::set_tx_delegate_take_rate_limit(tx_rate_limit); + log::debug!( "TxRateLimitDelegateTakeSet( tx_delegate_take_rate_limit: {:?} ) ", tx_rate_limit ); @@ -926,8 +934,8 @@ pub mod pallet { #[pallet::weight((0, DispatchClass::Operational, Pays::No))] pub fn sudo_set_min_delegate_take(origin: OriginFor, take: u16) -> DispatchResult { ensure_root(origin)?; - T::Subtensor::set_min_delegate_take(take); - log::info!("TxMinDelegateTakeSet( tx_min_delegate_take: {:?} ) ", take); + pallet_subtensor::Pallet::::set_min_delegate_take(take); + log::debug!("TxMinDelegateTakeSet( tx_min_delegate_take: {:?} ) ", take); Ok(()) } @@ -941,8 +949,10 @@ pub mod pallet { target_stakes_per_interval: u64, ) -> DispatchResult { ensure_root(origin)?; - T::Subtensor::set_target_stakes_per_interval(target_stakes_per_interval); - log::info!( + pallet_subtensor::Pallet::::set_target_stakes_per_interval( + target_stakes_per_interval, + ); + log::debug!( "TxTargetStakesPerIntervalSet( set_target_stakes_per_interval: {:?} ) ", target_stakes_per_interval ); @@ -959,15 +969,15 @@ pub mod pallet { netuid: u16, interval: u64, ) -> DispatchResult { - T::Subtensor::ensure_subnet_owner_or_root(origin, netuid)?; + pallet_subtensor::Pallet::::ensure_subnet_owner_or_root(origin, netuid)?; ensure!( - T::Subtensor::if_subnet_exist(netuid), + pallet_subtensor::Pallet::::if_subnet_exist(netuid), Error::::SubnetDoesNotExist ); - T::Subtensor::set_commit_reveal_weights_interval(netuid, interval); - log::info!( + pallet_subtensor::Pallet::::set_commit_reveal_weights_interval(netuid, interval); + log::debug!( "SetWeightCommitInterval( netuid: {:?}, interval: {:?} ) ", netuid, interval @@ -985,15 +995,15 @@ pub mod pallet { netuid: u16, enabled: bool, ) -> DispatchResult { - T::Subtensor::ensure_subnet_owner_or_root(origin, netuid)?; + pallet_subtensor::Pallet::::ensure_subnet_owner_or_root(origin, netuid)?; ensure!( - T::Subtensor::if_subnet_exist(netuid), + pallet_subtensor::Pallet::::if_subnet_exist(netuid), Error::::SubnetDoesNotExist ); - T::Subtensor::set_commit_reveal_weights_enabled(netuid, enabled); - log::info!("ToggleSetWeightsCommitReveal( netuid: {:?} ) ", netuid); + pallet_subtensor::Pallet::::set_commit_reveal_weights_enabled(netuid, enabled); + log::debug!("ToggleSetWeightsCommitReveal( netuid: {:?} ) ", netuid); Ok(()) } @@ -1013,9 +1023,9 @@ pub mod pallet { netuid: u16, enabled: bool, ) -> DispatchResult { - T::Subtensor::ensure_subnet_owner_or_root(origin, netuid)?; - T::Subtensor::set_liquid_alpha_enabled(netuid, enabled); - log::info!( + pallet_subtensor::Pallet::::ensure_subnet_owner_or_root(origin, netuid)?; + pallet_subtensor::Pallet::::set_liquid_alpha_enabled(netuid, enabled); + log::debug!( "LiquidAlphaEnableToggled( netuid: {:?}, Enabled: {:?} ) ", netuid, enabled @@ -1032,8 +1042,10 @@ pub mod pallet { alpha_low: u16, alpha_high: u16, ) -> DispatchResult { - T::Subtensor::ensure_subnet_owner_or_root(origin.clone(), netuid)?; - T::Subtensor::do_set_alpha_values(origin, netuid, alpha_low, alpha_high) + pallet_subtensor::Pallet::::ensure_subnet_owner_or_root(origin.clone(), netuid)?; + pallet_subtensor::Pallet::::do_set_alpha_values( + origin, netuid, alpha_low, alpha_high, + ) } /// Sets the hotkey emission tempo. @@ -1058,8 +1070,8 @@ pub mod pallet { emission_tempo: u64, ) -> DispatchResult { ensure_root(origin)?; - T::Subtensor::set_hotkey_emission_tempo(emission_tempo); - log::info!( + pallet_subtensor::Pallet::::set_hotkey_emission_tempo(emission_tempo); + log::debug!( "HotkeyEmissionTempoSet( emission_tempo: {:?} )", emission_tempo ); @@ -1105,7 +1117,7 @@ pub mod pallet { ensure_root(origin)?; // Set the new maximum stake for the specified network - T::Subtensor::set_network_max_stake(netuid, max_stake); + pallet_subtensor::Pallet::::set_network_max_stake(netuid, max_stake); // Log the change log::trace!( @@ -1133,91 +1145,3 @@ pub trait AuraInterface { impl AuraInterface for () { fn change_authorities(_: BoundedVec) {} } - -/////////////////////////////////////////// - -pub trait SubtensorInterface { - fn set_min_delegate_take(take: u16); - fn set_max_delegate_take(take: u16); - fn set_tx_rate_limit(rate_limit: u64); - fn set_tx_delegate_take_rate_limit(rate_limit: u64); - - fn set_serving_rate_limit(netuid: u16, rate_limit: u64); - - fn set_max_burn(netuid: u16, max_burn: u64); - fn set_min_burn(netuid: u16, min_burn: u64); - fn set_burn(netuid: u16, burn: u64); - - fn set_max_difficulty(netuid: u16, max_diff: u64); - fn set_min_difficulty(netuid: u16, min_diff: u64); - fn set_difficulty(netuid: u16, diff: u64); - - fn set_weights_rate_limit(netuid: u16, rate_limit: u64); - - fn set_weights_version_key(netuid: u16, version: u64); - - fn set_bonds_moving_average(netuid: u16, moving_average: u64); - - fn set_max_allowed_validators(netuid: u16, max_validators: u16); - - fn get_root_netuid() -> u16; - fn if_subnet_exist(netuid: u16) -> bool; - fn create_account_if_non_existent(coldkey: &AccountId, hotkey: &AccountId); - fn coldkey_owns_hotkey(coldkey: &AccountId, hotkey: &AccountId) -> bool; - fn increase_stake_on_coldkey_hotkey_account( - coldkey: &AccountId, - hotkey: &AccountId, - increment: u64, - ); - fn add_balance_to_coldkey_account(coldkey: &AccountId, amount: Balance); - fn get_current_block_as_u64() -> u64; - fn get_subnetwork_n(netuid: u16) -> u16; - fn get_max_allowed_uids(netuid: u16) -> u16; - fn append_neuron(netuid: u16, new_hotkey: &AccountId, block_number: u64); - fn get_neuron_to_prune(netuid: u16) -> u16; - fn replace_neuron(netuid: u16, uid_to_replace: u16, new_hotkey: &AccountId, block_number: u64); - fn set_total_issuance(total_issuance: u64); - fn set_network_immunity_period(net_immunity_period: u64); - fn set_network_min_lock(net_min_lock: u64); - fn set_rao_recycled(netuid: u16, rao_recycled: u64); - fn set_subnet_limit(limit: u16); - fn is_hotkey_registered_on_network(netuid: u16, hotkey: &AccountId) -> bool; - fn set_lock_reduction_interval(interval: u64); - fn set_tempo(netuid: u16, tempo: u16); - fn set_subnet_owner_cut(subnet_owner_cut: u16); - fn set_network_rate_limit(limit: u64); - fn set_max_registrations_per_block(netuid: u16, max_registrations_per_block: u16); - fn set_adjustment_alpha(netuid: u16, adjustment_alpha: u64); - fn set_target_registrations_per_interval(netuid: u16, target_registrations_per_interval: u16); - fn set_network_pow_registration_allowed(netuid: u16, registration_allowed: bool); - fn set_network_registration_allowed(netuid: u16, registration_allowed: bool); - fn set_activity_cutoff(netuid: u16, activity_cutoff: u16); - fn ensure_subnet_owner_or_root(o: RuntimeOrigin, netuid: u16) -> Result<(), DispatchError>; - fn set_rho(netuid: u16, rho: u16); - fn set_kappa(netuid: u16, kappa: u16); - fn set_max_allowed_uids(netuid: u16, max_allowed: u16); - fn set_min_allowed_weights(netuid: u16, min_allowed_weights: u16); - fn set_immunity_period(netuid: u16, immunity_period: u16); - fn set_max_weight_limit(netuid: u16, max_weight_limit: u16); - fn set_scaling_law_power(netuid: u16, scaling_law_power: u16); - fn set_validator_prune_len(netuid: u16, validator_prune_len: u64); - fn set_adjustment_interval(netuid: u16, adjustment_interval: u16); - fn set_weights_set_rate_limit(netuid: u16, weights_set_rate_limit: u64); - fn init_new_network(netuid: u16, tempo: u16); - fn set_weights_min_stake(min_stake: u64); - fn get_nominator_min_required_stake() -> u64; - fn set_nominator_min_required_stake(min_stake: u64); - fn clear_small_nominations(); - fn set_target_stakes_per_interval(target_stakes_per_interval: u64); - fn set_commit_reveal_weights_interval(netuid: u16, interval: u64); - fn set_commit_reveal_weights_enabled(netuid: u16, enabled: bool); - fn set_liquid_alpha_enabled(netuid: u16, enabled: bool); - fn do_set_alpha_values( - origin: RuntimeOrigin, - netuid: u16, - alpha_low: u16, - alpha_high: u16, - ) -> Result<(), DispatchError>; - fn set_hotkey_emission_tempo(emission_tempo: u64); - fn set_network_max_stake(netuid: u16, max_stake: u64); -} diff --git a/pallets/admin-utils/tests/mock.rs b/pallets/admin-utils/tests/mock.rs index 7ae11b6fb..342ed01cd 100644 --- a/pallets/admin-utils/tests/mock.rs +++ b/pallets/admin-utils/tests/mock.rs @@ -2,18 +2,20 @@ use frame_support::{ assert_ok, derive_impl, parameter_types, - traits::{Everything, Hooks}, + traits::{Everything, Hooks, PrivilegeCmp}, weights, }; use frame_system as system; -use frame_system::{limits, EnsureNever}; +use frame_system::{limits, EnsureNever, EnsureRoot}; use sp_consensus_aura::sr25519::AuthorityId as AuraId; use sp_core::U256; use sp_core::{ConstU64, H256}; use sp_runtime::{ traits::{BlakeTwo256, ConstU32, IdentityLookup}, - BuildStorage, DispatchError, + BuildStorage, Perbill, }; +use sp_std::cmp::Ordering; +use sp_weights::Weight; type Block = frame_system::mocking::MockBlock; @@ -25,6 +27,7 @@ frame_support::construct_runtime!( Balances: pallet_balances, AdminUtils: pallet_admin_utils, SubtensorModule: pallet_subtensor::{Pallet, Call, Storage, Event, Error}, + Scheduler: pallet_scheduler::{Pallet, Call, Storage, Event}, } ); @@ -77,12 +80,16 @@ parameter_types! { pub const InitialBondsMovingAverage: u64 = 900_000; pub const InitialStakePruningMin: u16 = 0; pub const InitialFoundationDistribution: u64 = 0; - pub const InitialDefaultTake: u16 = 11_796; // 18% honest number. - pub const InitialMinTake: u16 = 5_898; // 9%; + pub const InitialDefaultDelegateTake: u16 = 11_796; // 18% honest number. + pub const InitialMinDelegateTake: u16 = 5_898; // 9%; + pub const InitialDefaultChildKeyTake: u16 = 0; // Allow 0 % + pub const InitialMinChildKeyTake: u16 = 0; // Allow 0 % + pub const InitialMaxChildKeyTake: u16 = 11_796; // 18 %; pub const InitialWeightsVersionKey: u16 = 0; pub const InitialServingRateLimit: u64 = 0; // No limit. pub const InitialTxRateLimit: u64 = 0; // Disable rate limit for testing pub const InitialTxDelegateTakeRateLimit: u64 = 0; // Disable rate limit for testing + pub const InitialTxChildKeyTakeRateLimit: u64 = 0; // Disable rate limit for testing pub const InitialBurn: u64 = 0; pub const InitialMinBurn: u64 = 0; pub const InitialMaxBurn: u64 = 1_000_000_000; @@ -115,18 +122,21 @@ parameter_types! { pub const InitialAlphaLow: u16 = 45875; // Represents 0.7 as per the production default pub const InitialLiquidAlphaOn: bool = false; // Default value for LiquidAlphaOn pub const InitialHotkeyEmissionTempo: u64 = 1; - pub const InitialNetworkMaxStake: u64 = 500_000_000_000_000; // 500_000 TAO + pub const InitialNetworkMaxStake: u64 = u64::MAX; // Maximum possible value for u64, this make the make stake infinity + pub const InitialColdkeySwapScheduleDuration: u64 = 5 * 24 * 60 * 60 / 12; // 5 days + pub const InitialDissolveNetworkScheduleDuration: u64 = 5 * 24 * 60 * 60 / 12; // 5 days } impl pallet_subtensor::Config for Test { type RuntimeEvent = RuntimeEvent; + type RuntimeCall = RuntimeCall; type Currency = Balances; type InitialIssuance = InitialIssuance; type SudoRuntimeCall = TestRuntimeCall; type CouncilOrigin = EnsureNever; type SenateMembers = (); type TriumvirateInterface = (); - + type Scheduler = Scheduler; type InitialMinAllowedWeights = InitialMinAllowedWeights; type InitialEmissionValue = InitialEmissionValue; type InitialMaxWeightsLimit = InitialMaxWeightsLimit; @@ -146,14 +156,18 @@ impl pallet_subtensor::Config for Test { type InitialPruningScore = InitialPruningScore; type InitialBondsMovingAverage = InitialBondsMovingAverage; type InitialMaxAllowedValidators = InitialMaxAllowedValidators; - type InitialDefaultTake = InitialDefaultTake; - type InitialMinTake = InitialMinTake; + type InitialDefaultDelegateTake = InitialDefaultDelegateTake; + type InitialMinDelegateTake = InitialMinDelegateTake; + type InitialDefaultChildKeyTake = InitialDefaultChildKeyTake; + type InitialMinChildKeyTake = InitialMinChildKeyTake; + type InitialMaxChildKeyTake = InitialMaxChildKeyTake; type InitialWeightsVersionKey = InitialWeightsVersionKey; type InitialMaxDifficulty = InitialMaxDifficulty; type InitialMinDifficulty = InitialMinDifficulty; type InitialServingRateLimit = InitialServingRateLimit; type InitialTxRateLimit = InitialTxRateLimit; type InitialTxDelegateTakeRateLimit = InitialTxDelegateTakeRateLimit; + type InitialTxChildKeyTakeRateLimit = InitialTxChildKeyTakeRateLimit; type InitialBurn = InitialBurn; type InitialMaxBurn = InitialMaxBurn; type InitialMinBurn = InitialMinBurn; @@ -173,6 +187,9 @@ impl pallet_subtensor::Config for Test { type LiquidAlphaOn = InitialLiquidAlphaOn; type InitialHotkeyEmissionTempo = InitialHotkeyEmissionTempo; type InitialNetworkMaxStake = InitialNetworkMaxStake; + type Preimages = (); + type InitialColdkeySwapScheduleDuration = InitialColdkeySwapScheduleDuration; + type InitialDissolveNetworkScheduleDuration = InitialDissolveNetworkScheduleDuration; } #[derive_impl(frame_system::config_preludes::TestDefaultConfig)] @@ -218,281 +235,11 @@ impl pallet_balances::Config for Test { type RuntimeHoldReason = (); } -pub struct SubtensorIntrf; - -impl pallet_admin_utils::SubtensorInterface for SubtensorIntrf { - fn set_max_delegate_take(default_take: u16) { - SubtensorModule::set_max_delegate_take(default_take); - } - - fn set_min_delegate_take(default_take: u16) { - SubtensorModule::set_min_delegate_take(default_take); - } - - fn set_tx_rate_limit(rate_limit: u64) { - SubtensorModule::set_tx_rate_limit(rate_limit); - } - - fn set_tx_delegate_take_rate_limit(rate_limit: u64) { - SubtensorModule::set_tx_delegate_take_rate_limit(rate_limit); - } - - fn set_serving_rate_limit(netuid: u16, rate_limit: u64) { - SubtensorModule::set_serving_rate_limit(netuid, rate_limit); - } - - fn set_max_burn(netuid: u16, max_burn: u64) { - SubtensorModule::set_max_burn(netuid, max_burn); - } - - fn set_min_burn(netuid: u16, min_burn: u64) { - SubtensorModule::set_min_burn(netuid, min_burn); - } - - fn set_burn(netuid: u16, burn: u64) { - SubtensorModule::set_burn(netuid, burn); - } - - fn set_max_difficulty(netuid: u16, max_diff: u64) { - SubtensorModule::set_max_difficulty(netuid, max_diff); - } - - fn set_min_difficulty(netuid: u16, min_diff: u64) { - SubtensorModule::set_min_difficulty(netuid, min_diff); - } - - fn set_difficulty(netuid: u16, diff: u64) { - SubtensorModule::set_difficulty(netuid, diff); - } - - fn set_weights_rate_limit(netuid: u16, rate_limit: u64) { - SubtensorModule::set_weights_set_rate_limit(netuid, rate_limit); - } - - fn set_weights_version_key(netuid: u16, version: u64) { - SubtensorModule::set_weights_version_key(netuid, version); - } - - fn set_bonds_moving_average(netuid: u16, moving_average: u64) { - SubtensorModule::set_bonds_moving_average(netuid, moving_average); - } - - fn set_max_allowed_validators(netuid: u16, max_validators: u16) { - SubtensorModule::set_max_allowed_validators(netuid, max_validators); - } - - fn get_root_netuid() -> u16 { - SubtensorModule::get_root_netuid() - } - - fn if_subnet_exist(netuid: u16) -> bool { - SubtensorModule::if_subnet_exist(netuid) - } - - fn create_account_if_non_existent(coldkey: &AccountId, hotkey: &AccountId) { - SubtensorModule::create_account_if_non_existent(coldkey, hotkey) - } - - fn coldkey_owns_hotkey(coldkey: &AccountId, hotkey: &AccountId) -> bool { - SubtensorModule::coldkey_owns_hotkey(coldkey, hotkey) - } - - fn increase_stake_on_coldkey_hotkey_account( - coldkey: &AccountId, - hotkey: &AccountId, - increment: u64, - ) { - SubtensorModule::increase_stake_on_coldkey_hotkey_account(coldkey, hotkey, increment); - } - - fn add_balance_to_coldkey_account(coldkey: &AccountId, amount: Balance) { - SubtensorModule::add_balance_to_coldkey_account(coldkey, amount); - } - - fn get_current_block_as_u64() -> u64 { - SubtensorModule::get_current_block_as_u64() - } - - fn get_subnetwork_n(netuid: u16) -> u16 { - SubtensorModule::get_subnetwork_n(netuid) - } - - fn get_max_allowed_uids(netuid: u16) -> u16 { - SubtensorModule::get_max_allowed_uids(netuid) - } - - fn append_neuron(netuid: u16, new_hotkey: &AccountId, block_number: u64) { - SubtensorModule::append_neuron(netuid, new_hotkey, block_number) - } - - fn get_neuron_to_prune(netuid: u16) -> u16 { - SubtensorModule::get_neuron_to_prune(netuid) - } - - fn replace_neuron(netuid: u16, uid_to_replace: u16, new_hotkey: &AccountId, block_number: u64) { - SubtensorModule::replace_neuron(netuid, uid_to_replace, new_hotkey, block_number); - } - - fn set_total_issuance(total_issuance: u64) { - SubtensorModule::set_total_issuance(total_issuance); - } - - fn set_network_immunity_period(net_immunity_period: u64) { - SubtensorModule::set_network_immunity_period(net_immunity_period); - } - - fn set_network_min_lock(net_min_lock: u64) { - SubtensorModule::set_network_min_lock(net_min_lock); - } - - fn set_subnet_limit(limit: u16) { - SubtensorModule::set_max_subnets(limit); - } - - fn set_lock_reduction_interval(interval: u64) { - SubtensorModule::set_lock_reduction_interval(interval); - } - - fn set_tempo(netuid: u16, tempo: u16) { - SubtensorModule::set_tempo(netuid, tempo); - } - - fn set_subnet_owner_cut(subnet_owner_cut: u16) { - SubtensorModule::set_subnet_owner_cut(subnet_owner_cut); - } - - fn set_network_rate_limit(limit: u64) { - SubtensorModule::set_network_rate_limit(limit); - } - - fn set_max_registrations_per_block(netuid: u16, max_registrations_per_block: u16) { - SubtensorModule::set_max_registrations_per_block(netuid, max_registrations_per_block); - } - - fn set_adjustment_alpha(netuid: u16, adjustment_alpha: u64) { - SubtensorModule::set_adjustment_alpha(netuid, adjustment_alpha); - } - - fn set_target_registrations_per_interval(netuid: u16, target_registrations_per_interval: u16) { - SubtensorModule::set_target_registrations_per_interval( - netuid, - target_registrations_per_interval, - ); - } - - fn set_network_pow_registration_allowed(netuid: u16, registration_allowed: bool) { - SubtensorModule::set_network_pow_registration_allowed(netuid, registration_allowed); - } - - fn set_network_registration_allowed(netuid: u16, registration_allowed: bool) { - SubtensorModule::set_network_pow_registration_allowed(netuid, registration_allowed); - } - - fn set_activity_cutoff(netuid: u16, activity_cutoff: u16) { - SubtensorModule::set_activity_cutoff(netuid, activity_cutoff); - } - - fn ensure_subnet_owner_or_root(o: RuntimeOrigin, netuid: u16) -> Result<(), DispatchError> { - SubtensorModule::ensure_subnet_owner_or_root(o, netuid) - } - - fn set_rho(netuid: u16, rho: u16) { - SubtensorModule::set_rho(netuid, rho); - } - - fn set_kappa(netuid: u16, kappa: u16) { - SubtensorModule::set_kappa(netuid, kappa); - } - - fn set_max_allowed_uids(netuid: u16, max_allowed: u16) { - SubtensorModule::set_max_allowed_uids(netuid, max_allowed); - } - - fn set_min_allowed_weights(netuid: u16, min_allowed_weights: u16) { - SubtensorModule::set_min_allowed_weights(netuid, min_allowed_weights); - } - - fn set_immunity_period(netuid: u16, immunity_period: u16) { - SubtensorModule::set_immunity_period(netuid, immunity_period); - } - - fn set_max_weight_limit(netuid: u16, max_weight_limit: u16) { - SubtensorModule::set_max_weight_limit(netuid, max_weight_limit); - } - - fn set_scaling_law_power(netuid: u16, scaling_law_power: u16) { - SubtensorModule::set_scaling_law_power(netuid, scaling_law_power); - } - - fn set_validator_prune_len(netuid: u16, validator_prune_len: u64) { - SubtensorModule::set_validator_prune_len(netuid, validator_prune_len); - } - - fn set_adjustment_interval(netuid: u16, adjustment_interval: u16) { - SubtensorModule::set_adjustment_interval(netuid, adjustment_interval); - } +pub struct OriginPrivilegeCmp; - fn set_weights_set_rate_limit(netuid: u16, weights_set_rate_limit: u64) { - SubtensorModule::set_weights_set_rate_limit(netuid, weights_set_rate_limit); - } - - fn set_rao_recycled(netuid: u16, rao_recycled: u64) { - SubtensorModule::set_rao_recycled(netuid, rao_recycled); - } - - fn is_hotkey_registered_on_network(netuid: u16, hotkey: &AccountId) -> bool { - SubtensorModule::is_hotkey_registered_on_network(netuid, hotkey) - } - - fn init_new_network(netuid: u16, tempo: u16) { - SubtensorModule::init_new_network(netuid, tempo); - } - - fn set_weights_min_stake(min_stake: u64) { - SubtensorModule::set_weights_min_stake(min_stake); - } - - fn set_nominator_min_required_stake(min_stake: u64) { - SubtensorModule::set_nominator_min_required_stake(min_stake); - } - - fn get_nominator_min_required_stake() -> u64 { - SubtensorModule::get_nominator_min_required_stake() - } - - fn clear_small_nominations() { - SubtensorModule::clear_small_nominations(); - } - - fn set_target_stakes_per_interval(target_stakes_per_interval: u64) { - SubtensorModule::set_target_stakes_per_interval(target_stakes_per_interval); - } - - fn set_commit_reveal_weights_interval(netuid: u16, interval: u64) { - SubtensorModule::set_commit_reveal_weights_interval(netuid, interval); - } - - fn set_commit_reveal_weights_enabled(netuid: u16, enabled: bool) { - SubtensorModule::set_commit_reveal_weights_enabled(netuid, enabled); - } - - fn set_liquid_alpha_enabled(netuid: u16, enabled: bool) { - SubtensorModule::set_liquid_alpha_enabled(netuid, enabled); - } - fn set_hotkey_emission_tempo(emission_tempo: u64) { - SubtensorModule::set_hotkey_emission_tempo(emission_tempo) - } - fn set_network_max_stake(netuid: u16, max_stake: u64) { - SubtensorModule::set_network_max_stake(netuid, max_stake) - } - - fn do_set_alpha_values( - origin: RuntimeOrigin, - netuid: u16, - alpha_low: u16, - alpha_high: u16, - ) -> Result<(), DispatchError> { - SubtensorModule::do_set_alpha_values(origin, netuid, alpha_low, alpha_high) +impl PrivilegeCmp for OriginPrivilegeCmp { + fn cmp_privilege(_left: &OriginCaller, _right: &OriginCaller) -> Option { + None } } @@ -502,10 +249,29 @@ impl pallet_admin_utils::Config for Test { type MaxAuthorities = ConstU32<32>; type Aura = (); type Balance = Balance; - type Subtensor = SubtensorIntrf; type WeightInfo = (); } +parameter_types! { + pub MaximumSchedulerWeight: Weight = Perbill::from_percent(80) * + BlockWeights::get().max_block; + pub const MaxScheduledPerBlock: u32 = 50; + pub const NoPreimagePostponement: Option = Some(10); +} + +impl pallet_scheduler::Config for Test { + type RuntimeOrigin = RuntimeOrigin; + type RuntimeEvent = RuntimeEvent; + type PalletsOrigin = OriginCaller; + type RuntimeCall = RuntimeCall; + type MaximumWeight = MaximumSchedulerWeight; + type ScheduleOrigin = EnsureRoot; + type MaxScheduledPerBlock = MaxScheduledPerBlock; + type WeightInfo = pallet_scheduler::weights::SubstrateWeight; + type OriginPrivilegeCmp = OriginPrivilegeCmp; + type Preimages = (); +} + // Build genesis storage according to the mock runtime. pub fn new_test_ext() -> sp_io::TestExternalities { sp_tracing::try_init_simple(); diff --git a/pallets/admin-utils/tests/tests.rs b/pallets/admin-utils/tests/tests.rs index 6e78a1ed6..af3bf66d7 100644 --- a/pallets/admin-utils/tests/tests.rs +++ b/pallets/admin-utils/tests/tests.rs @@ -16,7 +16,7 @@ use mock::*; fn test_sudo_set_default_take() { new_test_ext().execute_with(|| { let to_be_set: u16 = 10; - let init_value: u16 = SubtensorModule::get_default_take(); + let init_value: u16 = SubtensorModule::get_default_delegate_take(); assert_eq!( AdminUtils::sudo_set_default_take( <::RuntimeOrigin>::signed(U256::from(0)), @@ -24,12 +24,12 @@ fn test_sudo_set_default_take() { ), Err(DispatchError::BadOrigin) ); - assert_eq!(SubtensorModule::get_default_take(), init_value); + assert_eq!(SubtensorModule::get_default_delegate_take(), init_value); assert_ok!(AdminUtils::sudo_set_default_take( <::RuntimeOrigin>::root(), to_be_set )); - assert_eq!(SubtensorModule::get_default_take(), to_be_set); + assert_eq!(SubtensorModule::get_default_delegate_take(), to_be_set); }); } diff --git a/pallets/collective/src/lib.rs b/pallets/collective/src/lib.rs index 6aae3c85e..823e92663 100644 --- a/pallets/collective/src/lib.rs +++ b/pallets/collective/src/lib.rs @@ -165,6 +165,7 @@ pub struct Votes { /// The hard end time of this vote. end: BlockNumber, } + #[deny(missing_docs)] #[frame_support::pallet] pub mod pallet { diff --git a/pallets/commitments/src/types.rs b/pallets/commitments/src/types.rs index 912a474c0..06bafcaac 100644 --- a/pallets/commitments/src/types.rs +++ b/pallets/commitments/src/types.rs @@ -299,6 +299,7 @@ pub struct CommitmentInfo> { /// /// NOTE: This is stored separately primarily to facilitate the addition of extra fields in a /// backwards compatible way through a specialized `Decode` impl. +#[freeze_struct("632f12850e51c420")] #[derive( CloneNoBound, Encode, Eq, MaxEncodedLen, PartialEqNoBound, RuntimeDebugNoBound, TypeInfo, )] diff --git a/pallets/registry/src/types.rs b/pallets/registry/src/types.rs index 0badd5669..58cc5ed19 100644 --- a/pallets/registry/src/types.rs +++ b/pallets/registry/src/types.rs @@ -367,6 +367,7 @@ impl> IdentityInfo { /// /// NOTE: This is stored separately primarily to facilitate the addition of extra fields in a /// backwards compatible way through a specialized `Decode` impl. +#[freeze_struct("797b69e82710bb21")] #[derive( CloneNoBound, Encode, Eq, MaxEncodedLen, PartialEqNoBound, RuntimeDebugNoBound, TypeInfo, )] diff --git a/pallets/subtensor/Cargo.toml b/pallets/subtensor/Cargo.toml index a0835008f..3023d1e0d 100644 --- a/pallets/subtensor/Cargo.toml +++ b/pallets/subtensor/Cargo.toml @@ -28,6 +28,7 @@ frame-support = { workspace = true } frame-system = { workspace = true } sp-io = { workspace = true } serde = { workspace = true, features = ["derive"] } +serde_json = { workspace = true } serde-tuple-vec-map = { workspace = true } serde_bytes = { workspace = true, features = ["alloc"] } serde_with = { workspace = true, features = ["macros"] } @@ -48,12 +49,15 @@ num-traits = { version = "0.2.19", default-features = false, features = ["libm"] [dev-dependencies] pallet-balances = { workspace = true, features = ["std"] } +pallet-scheduler = { workspace = true } sp-version = { workspace = true } # Substrate sp-tracing = { workspace = true } parity-util-mem = { workspace = true, features = ["primitive-types"] } rand = { workspace = true } sp-core = { workspace = true } +sp-std = { workspace = true } +pallet-preimage = { workspace = true } [features] default = ["std"] @@ -67,6 +71,8 @@ std = [ "pallet-membership/std", "substrate-fixed/std", "pallet-balances/std", + "pallet-preimage/std", + "pallet-scheduler/std", "pallet-transaction-payment/std", "pallet-utility/std", "sp-core/std", @@ -83,6 +89,7 @@ std = [ "serde_with/std", "substrate-fixed/std", "num-traits/std", + "serde_json/std" ] runtime-benchmarks = [ "frame-benchmarking/runtime-benchmarks", @@ -92,13 +99,17 @@ runtime-benchmarks = [ "pallet-membership/runtime-benchmarks", "pallet-utility/runtime-benchmarks", "sp-runtime/runtime-benchmarks", - "pallet-collective/runtime-benchmarks" + "pallet-collective/runtime-benchmarks", + "pallet-preimage/runtime-benchmarks", + "pallet-scheduler/runtime-benchmarks", ] try-runtime = [ "frame-support/try-runtime", "frame-system/try-runtime", "pallet-balances/try-runtime", "pallet-membership/try-runtime", + "pallet-preimage/try-runtime", + "pallet-scheduler/try-runtime", "pallet-transaction-payment/try-runtime", "pallet-utility/try-runtime", "sp-runtime/try-runtime", diff --git a/pallets/subtensor/src/benchmarks.rs b/pallets/subtensor/src/benchmarks.rs index 03e087a92..e9d5f804c 100644 --- a/pallets/subtensor/src/benchmarks.rs +++ b/pallets/subtensor/src/benchmarks.rs @@ -159,7 +159,7 @@ benchmarks! { Subtensor::::add_balance_to_coldkey_account(&coldkey.clone(), wallet_bal); assert_ok!(Subtensor::::do_burned_registration(RawOrigin::Signed(coldkey.clone()).into(), netuid, hotkey.clone())); - assert_ok!(Subtensor::::do_become_delegate(RawOrigin::Signed(coldkey.clone()).into(), hotkey.clone(), Subtensor::::get_default_take())); + assert_ok!(Subtensor::::do_become_delegate(RawOrigin::Signed(coldkey.clone()).into(), hotkey.clone(), Subtensor::::get_default_delegate_take())); // Stake 10% of our current total staked TAO let u64_staked_amt = 100_000_000_000; @@ -429,4 +429,37 @@ reveal_weights { }: reveal_weights(RawOrigin::Signed(hotkey.clone()), netuid, uids, weight_values, salt, version_key) + schedule_swap_coldkey { + let old_coldkey: T::AccountId = account("old_cold", 0, 1); + let new_coldkey: T::AccountId = account("new_cold", 1, 2); + }: schedule_swap_coldkey(RawOrigin::Signed(old_coldkey.clone()), new_coldkey.clone()) + + schedule_dissolve_network { + let coldkey: T::AccountId = account("coldkey", 0, 1); + let netuid = 1; + }: schedule_dissolve_network(RawOrigin::Signed(coldkey.clone()), netuid) + benchmark_sudo_set_tx_childkey_take_rate_limit { + // We don't need to set up any initial state for this benchmark + // as it's a simple setter function that only requires root origin + let new_rate_limit: u64 = 100; +}: sudo_set_tx_childkey_take_rate_limit(RawOrigin::Root, new_rate_limit) + +benchmark_set_childkey_take { + // Setup + let netuid: u16 = 1; + let tempo: u16 = 1; + let seed: u32 = 1; + let coldkey: T::AccountId = account("Cold", 0, seed); + let hotkey: T::AccountId = account("Hot", 0, seed); + let take: u16 = 1000; // 10% in basis points + + // Initialize the network + Subtensor::::init_new_network(netuid, tempo); + + // Register the hotkey + Subtensor::::set_burn(netuid, 1); + let amount_to_be_staked = 1_000_000u32.into(); + Subtensor::::add_balance_to_coldkey_account(&coldkey, amount_to_be_staked); + assert_ok!(Subtensor::::do_burned_registration(RawOrigin::Signed(coldkey.clone()).into(), netuid, hotkey.clone())); +}: set_childkey_take(RawOrigin::Signed(coldkey), hotkey, netuid, take) } diff --git a/pallets/subtensor/src/coinbase/root.rs b/pallets/subtensor/src/coinbase/root.rs index 974931e8f..39f745b93 100644 --- a/pallets/subtensor/src/coinbase/root.rs +++ b/pallets/subtensor/src/coinbase/root.rs @@ -486,7 +486,7 @@ impl Pallet { // --- 1. Ensure that the call originates from a signed source and retrieve the caller's account ID (coldkey). let coldkey = ensure_signed(origin)?; - log::info!( + log::debug!( "do_root_register( coldkey: {:?}, hotkey: {:?} )", coldkey, hotkey @@ -529,7 +529,7 @@ impl Pallet { // --- 12.1.2 Add the new account and make them a member of the Senate. Self::append_neuron(root_netuid, &hotkey, current_block_number); - log::info!("add new neuron: {:?} on uid {:?}", hotkey, subnetwork_uid); + log::debug!("add new neuron: {:?} on uid {:?}", hotkey, subnetwork_uid); } else { // --- 13.1.1 The network is full. Perform replacement. // Find the neuron with the lowest stake value to replace. @@ -562,7 +562,7 @@ impl Pallet { // Replace the neuron account with new information. Self::replace_neuron(root_netuid, lowest_uid, &hotkey, current_block_number); - log::info!( + log::debug!( "replace neuron: {:?} with {:?} on uid {:?}", replaced_hotkey, hotkey, @@ -588,7 +588,7 @@ impl Pallet { RegistrationsThisBlock::::mutate(root_netuid, |val| *val += 1); // --- 16. Log and announce the successful registration. - log::info!( + log::debug!( "RootRegistered(netuid:{:?} uid:{:?} hotkey:{:?})", root_netuid, subnetwork_uid, @@ -622,7 +622,7 @@ impl Pallet { // --- 1. Ensure that the call originates from a signed source and retrieve the caller's account ID (coldkey). let coldkey = ensure_signed(origin)?; - log::info!( + log::debug!( "do_root_register( coldkey: {:?}, hotkey: {:?} )", coldkey, hotkey @@ -652,7 +652,7 @@ impl Pallet { } // --- 5. Log and announce the successful Senate adjustment. - log::info!( + log::debug!( "SenateAdjusted(old_hotkey:{:?} hotkey:{:?})", replaced, hotkey @@ -733,7 +733,7 @@ impl Pallet { ) -> dispatch::DispatchResult { // Check the caller's signature. This is the coldkey of a registered account. let coldkey = ensure_signed(origin)?; - log::info!( + log::debug!( "do_set_root_weights( origin:{:?} netuid:{:?}, uids:{:?}, values:{:?})", coldkey, netuid, @@ -834,7 +834,7 @@ impl Pallet { Self::set_last_update_for_uid(netuid, neuron_uid, current_block); // Emit the tracking event. - log::info!( + log::debug!( "RootWeightsSet( netuid:{:?}, neuron_uid:{:?} )", netuid, neuron_uid @@ -968,7 +968,7 @@ impl Pallet { SubnetOwner::::insert(netuid_to_register, coldkey); // --- 8. Emit the NetworkAdded event. - log::info!( + log::debug!( "NetworkAdded( netuid:{:?}, modality:{:?} )", netuid_to_register, 0 @@ -1012,7 +1012,7 @@ impl Pallet { Self::remove_network(netuid); // --- 5. Emit the NetworkRemoved event. - log::info!("NetworkRemoved( netuid:{:?} )", netuid); + log::debug!("NetworkRemoved( netuid:{:?} )", netuid); Self::deposit_event(Event::NetworkRemoved(netuid)); // --- 6. Return success. @@ -1274,7 +1274,7 @@ impl Pallet { } }); - log::info!("Netuids Order: {:?}", netuids); + log::debug!("Netuids Order: {:?}", netuids); match netuids.last() { Some(netuid) => *netuid, diff --git a/pallets/subtensor/src/coinbase/run_coinbase.rs b/pallets/subtensor/src/coinbase/run_coinbase.rs index fcf76728f..723edc423 100644 --- a/pallets/subtensor/src/coinbase/run_coinbase.rs +++ b/pallets/subtensor/src/coinbase/run_coinbase.rs @@ -131,6 +131,11 @@ impl Pallet { log::debug!("Accumulated emissions on hotkey {:?} for netuid {:?}: mining {:?}, validator {:?}", hotkey, *netuid, mining_emission, validator_emission); } } else { + // No epoch, increase blocks since last step and continue + Self::set_blocks_since_last_step( + *netuid, + Self::get_blocks_since_last_step(*netuid).saturating_add(1), + ); log::debug!("Tempo not reached for subnet: {:?}", *netuid); } } @@ -186,7 +191,7 @@ impl Pallet { mining_emission: u64, ) { // --- 1. First, calculate the hotkey's share of the emission. - let take_proportion: I64F64 = I64F64::from_num(Delegates::::get(hotkey)) + let take_proportion: I64F64 = I64F64::from_num(Self::get_childkey_take(hotkey, netuid)) .saturating_div(I64F64::from_num(u16::MAX)); let hotkey_take: u64 = take_proportion .saturating_mul(I64F64::from_num(validating_emission)) diff --git a/pallets/subtensor/src/epoch/run_epoch.rs b/pallets/subtensor/src/epoch/run_epoch.rs index 1cb2c3448..d919c6dbb 100644 --- a/pallets/subtensor/src/epoch/run_epoch.rs +++ b/pallets/subtensor/src/epoch/run_epoch.rs @@ -31,12 +31,10 @@ impl Pallet { /// This function does not explicitly panic, but underlying arithmetic operations /// use saturating arithmetic to prevent overflows. /// - /// TODO: check for self loops. - /// TODO: (@distributedstatemachine): check if we should return error , otherwise self loop - /// detection is impossible to test. pub fn get_stake_for_hotkey_on_subnet(hotkey: &T::AccountId, netuid: u16) -> u64 { // Retrieve the initial total stake for the hotkey without any child/parent adjustments. let initial_stake: u64 = Self::get_total_stake_for_hotkey(hotkey); + log::debug!("Initial stake: {:?}", initial_stake); let mut stake_to_children: u64 = 0; let mut stake_from_parents: u64 = 0; @@ -1310,7 +1308,7 @@ impl Pallet { AlphaValues::::insert(netuid, (alpha_low, alpha_high)); - log::info!( + log::debug!( "AlphaValuesSet( netuid: {:?}, AlphaLow: {:?}, AlphaHigh: {:?} ) ", netuid, alpha_low, diff --git a/pallets/subtensor/src/lib.rs b/pallets/subtensor/src/lib.rs index abf6a8613..02fa4d7d6 100644 --- a/pallets/subtensor/src/lib.rs +++ b/pallets/subtensor/src/lib.rs @@ -19,6 +19,7 @@ use codec::{Decode, Encode}; use frame_support::sp_runtime::transaction_validity::InvalidTransaction; use frame_support::sp_runtime::transaction_validity::ValidTransaction; use pallet_balances::Call as BalancesCall; +// use pallet_scheduler as Scheduler; use scale_info::TypeInfo; use sp_runtime::{ traits::{DispatchInfoOf, Dispatchable, PostDispatchInfoOf, SignedExtension}, @@ -44,7 +45,7 @@ pub mod staking; pub mod subnets; pub mod swap; pub mod utils; -use crate::utils::TransactionType; +use crate::utils::rate_limiting::TransactionType; use macros::{config, dispatches, errors, events, genesis, hooks}; // apparently this is stabilized since rust 1.36 @@ -59,16 +60,17 @@ extern crate alloc; #[import_section(config::config)] #[frame_support::pallet] pub mod pallet { - use crate::migrations; use frame_support::{ dispatch::GetDispatchInfo, pallet_prelude::{DispatchResult, StorageMap, ValueQuery, *}, - traits::{tokens::fungible, UnfilteredDispatchable}, + traits::{ + tokens::fungible, OriginTrait, QueryPreimage, StorePreimage, UnfilteredDispatchable, + }, }; use frame_system::pallet_prelude::*; use sp_core::H256; - use sp_runtime::traits::TrailingZeroInput; + use sp_runtime::traits::{Dispatchable, TrailingZeroInput}; use sp_std::vec; use sp_std::vec::Vec; @@ -77,6 +79,13 @@ pub mod pallet { #[cfg(feature = "std")] use sp_std::prelude::Box; + /// Origin for the pallet + pub type PalletsOriginOf = + <::RuntimeOrigin as OriginTrait>::PalletsOrigin; + + /// Call type for the pallet + pub type CallOf = ::RuntimeCall; + /// Tracks version for migrations. Should be monotonic with respect to the /// order of migrations. (i.e. always increasing) const STORAGE_VERSION: StorageVersion = StorageVersion::new(7); @@ -95,7 +104,11 @@ pub mod pallet { /// Struct for Axon. pub type AxonInfoOf = AxonInfo; + /// local one + pub type LocalCallOf = ::RuntimeCall; + /// Data structure for Axon information. + #[crate::freeze_struct("3545cfb0cac4c1f5")] #[derive(Encode, Decode, Default, TypeInfo, Clone, PartialEq, Eq, Debug)] pub struct AxonInfo { /// Axon serving block. @@ -118,7 +131,9 @@ pub mod pallet { /// Struct for Prometheus. pub type PrometheusInfoOf = PrometheusInfo; + /// Data structure for Prometheus information. + #[crate::freeze_struct("5dde687e63baf0cd")] #[derive(Encode, Decode, Default, TypeInfo, Clone, PartialEq, Eq, Debug)] pub struct PrometheusInfo { /// Prometheus serving block. @@ -133,6 +148,27 @@ pub mod pallet { pub ip_type: u8, } + /// Struct for Prometheus. + pub type ChainIdentityOf = ChainIdentity; + + /// Data structure for Prometheus information. + #[crate::freeze_struct("bbfd00438dbe2b58")] + #[derive(Encode, Decode, Default, TypeInfo, Clone, PartialEq, Eq, Debug)] + pub struct ChainIdentity { + /// The name of the chain identity + pub name: Vec, + /// The URL associated with the chain identity + pub url: Vec, + /// The image representation of the chain identity + pub image: Vec, + /// The Discord information for the chain identity + pub discord: Vec, + /// A description of the chain identity + pub description: Vec, + /// Additional information about the chain identity + pub additional: Vec, + } + /// ============================ /// ==== Staking + Accounts ==== /// ============================ @@ -143,15 +179,34 @@ pub mod pallet { 21_000_000_000_000_000 } #[pallet::type_value] - /// Default total stake. - pub fn DefaultDefaultTake() -> u16 { - T::InitialDefaultTake::get() + /// Default Delegate Take. + pub fn DefaultDelegateTake() -> u16 { + T::InitialDefaultDelegateTake::get() } + #[pallet::type_value] - /// Default minimum take. - pub fn DefaultMinTake() -> u16 { - T::InitialMinTake::get() + /// Default childkey take. + pub fn DefaultChildKeyTake() -> u16 { + T::InitialDefaultChildKeyTake::get() } + #[pallet::type_value] + /// Default minimum delegate take. + pub fn DefaultMinDelegateTake() -> u16 { + T::InitialMinDelegateTake::get() + } + + #[pallet::type_value] + /// Default minimum childkey take. + pub fn DefaultMinChildKeyTake() -> u16 { + T::InitialMinChildKeyTake::get() + } + + #[pallet::type_value] + /// Default maximum childkey take. + pub fn DefaultMaxChildKeyTake() -> u16 { + T::InitialMaxChildKeyTake::get() + } + #[pallet::type_value] /// Default account take. pub fn DefaultAccountTake() -> u64 { @@ -341,8 +396,6 @@ pub mod pallet { } T::InitialNetworkRateLimit::get() } - // #[pallet::type_value] /// Default value for network max stake. - // pub fn DefaultNetworkMaxStake() -> u64 { T::InitialNetworkMaxStake::get() } #[pallet::type_value] /// Default value for emission values. pub fn DefaultEmissionValues() -> u64 { @@ -516,6 +569,11 @@ pub mod pallet { T::InitialTxDelegateTakeRateLimit::get() } #[pallet::type_value] + /// Default value for chidlkey take rate limiting + pub fn DefaultTxChildKeyTakeRateLimit() -> u64 { + T::InitialTxChildKeyTakeRateLimit::get() + } + #[pallet::type_value] /// Default value for last extrinsic block. pub fn DefaultLastTxBlock() -> u64 { 0 @@ -556,6 +614,26 @@ pub mod pallet { T::InitialNetworkMaxStake::get() } + #[pallet::type_value] + /// Default value for coldkey swap schedule duration + pub fn DefaultColdkeySwapScheduleDuration() -> BlockNumberFor { + T::InitialColdkeySwapScheduleDuration::get() + } + + #[pallet::storage] + pub type ColdkeySwapScheduleDuration = + StorageValue<_, BlockNumberFor, ValueQuery, DefaultColdkeySwapScheduleDuration>; + + #[pallet::type_value] + /// Default value for dissolve network schedule duration + pub fn DefaultDissolveNetworkScheduleDuration() -> BlockNumberFor { + T::InitialDissolveNetworkScheduleDuration::get() + } + + #[pallet::storage] + pub type DissolveNetworkScheduleDuration = + StorageValue<_, BlockNumberFor, ValueQuery, DefaultDissolveNetworkScheduleDuration>; + #[pallet::storage] pub type SenateRequiredStakePercentage = StorageValue<_, u64, ValueQuery, DefaultSenateRequiredStakePercentage>; @@ -563,14 +641,28 @@ pub mod pallet { /// ============================ /// ==== Staking Variables ==== /// ============================ + /// The Subtensor [`TotalIssuance`] represents the total issuance of tokens on the Bittensor network. + /// + /// It is comprised of three parts: + /// - The total amount of issued tokens, tracked in the TotalIssuance of the Balances pallet + /// - The total amount of tokens staked in the system, tracked in [`TotalStake`] + /// - The total amount of tokens locked up for subnet reg, tracked in [`TotalSubnetLocked`] attained by iterating over subnet lock. + /// + /// Eventually, Bittensor should migrate to using Holds afterwhich time we will not require this + /// separate accounting. #[pallet::storage] // --- ITEM ( total_issuance ) pub type TotalIssuance = StorageValue<_, u64, ValueQuery, DefaultTotalIssuance>; #[pallet::storage] // --- ITEM ( total_stake ) pub type TotalStake = StorageValue<_, u64, ValueQuery>; - #[pallet::storage] // --- ITEM ( default_take ) - pub type MaxTake = StorageValue<_, u16, ValueQuery, DefaultDefaultTake>; - #[pallet::storage] // --- ITEM ( min_take ) - pub type MinTake = StorageValue<_, u16, ValueQuery, DefaultMinTake>; + #[pallet::storage] // --- ITEM ( default_delegate_take ) + pub type MaxDelegateTake = StorageValue<_, u16, ValueQuery, DefaultDelegateTake>; + #[pallet::storage] // --- ITEM ( min_delegate_take ) + pub type MinDelegateTake = StorageValue<_, u16, ValueQuery, DefaultMinDelegateTake>; + #[pallet::storage] // --- ITEM ( default_childkey_take ) + pub type MaxChildkeyTake = StorageValue<_, u16, ValueQuery, DefaultMaxChildKeyTake>; + #[pallet::storage] // --- ITEM ( min_childkey_take ) + pub type MinChildkeyTake = StorageValue<_, u16, ValueQuery, DefaultMinChildKeyTake>; + #[pallet::storage] // --- ITEM ( global_block_emission ) pub type BlockEmission = StorageValue<_, u64, ValueQuery, DefaultBlockEmission>; #[pallet::storage] // --- ITEM (target_stakes_per_interval) @@ -603,7 +695,19 @@ pub mod pallet { #[pallet::storage] /// MAP ( hot ) --> take | Returns the hotkey delegation take. And signals that this key is open for delegation. pub type Delegates = - StorageMap<_, Blake2_128Concat, T::AccountId, u16, ValueQuery, DefaultDefaultTake>; + StorageMap<_, Blake2_128Concat, T::AccountId, u16, ValueQuery, DefaultDelegateTake>; + #[pallet::storage] + /// DMAP ( hot, netuid ) --> take | Returns the hotkey childkey take for a specific subnet + pub type ChildkeyTake = StorageDoubleMap< + _, + Blake2_128Concat, + T::AccountId, // First key: hotkey + Identity, + u16, // Second key: netuid + u16, // Value: take + ValueQuery, + >; + #[pallet::storage] /// DMAP ( hot, cold ) --> stake | Returns the stake under a coldkey prefixed by hotkey. pub type Stake = StorageDoubleMap< @@ -683,6 +787,10 @@ pub mod pallet { pub type OwnedHotkeys = StorageMap<_, Blake2_128Concat, T::AccountId, Vec, ValueQuery>; + #[pallet::storage] // --- DMAP ( cold ) --> () | Maps coldkey to if a coldkey swap is scheduled. + pub type ColdkeySwapScheduled = + StorageMap<_, Blake2_128Concat, T::AccountId, (), ValueQuery>; + /// ============================ /// ==== Global Parameters ===== /// ============================ @@ -921,10 +1029,14 @@ pub mod pallet { /// --- ITEM ( tx_rate_limit ) pub type TxRateLimit = StorageValue<_, u64, ValueQuery, DefaultTxRateLimit>; #[pallet::storage] - /// --- ITEM ( tx_rate_limit ) + /// --- ITEM ( tx_delegate_take_rate_limit ) pub type TxDelegateTakeRateLimit = StorageValue<_, u64, ValueQuery, DefaultTxDelegateTakeRateLimit>; #[pallet::storage] + /// --- ITEM ( tx_childkey_take_rate_limit ) + pub type TxChildkeyTakeRateLimit = + StorageValue<_, u64, ValueQuery, DefaultTxChildKeyTakeRateLimit>; + #[pallet::storage] /// --- MAP ( netuid ) --> Whether or not Liquid Alpha is enabled pub type LiquidAlphaOn = StorageMap<_, Blake2_128Concat, u16, bool, ValueQuery, DefaultLiquidAlpha>; @@ -1048,6 +1160,9 @@ pub mod pallet { PrometheusInfoOf, OptionQuery, >; + #[pallet::storage] // --- MAP ( coldkey ) --> identity + pub type Identities = + StorageMap<_, Blake2_128Concat, T::AccountId, ChainIdentityOf, OptionQuery>; /// ================================= /// ==== Axon / Promo Endpoints ===== @@ -1068,7 +1183,11 @@ pub mod pallet { pub type LastTxBlock = StorageMap<_, Identity, T::AccountId, u64, ValueQuery, DefaultLastTxBlock>; #[pallet::storage] - /// --- MAP ( key ) --> last_block + /// --- MAP ( key ) --> last_tx_block_childkey_take + pub type LastTxBlockChildKeyTake = + StorageMap<_, Identity, T::AccountId, u64, ValueQuery, DefaultLastTxBlock>; + #[pallet::storage] + /// --- MAP ( key ) --> last_tx_block_delegate_take pub type LastTxBlockDelegateTake = StorageMap<_, Identity, T::AccountId, u64, ValueQuery, DefaultLastTxBlock>; #[pallet::storage] @@ -1171,13 +1290,27 @@ pub enum CallType { Other, } +#[derive(Debug, PartialEq)] +pub enum CustomTransactionError { + ColdkeyInSwapSchedule, +} + +impl From for u8 { + fn from(variant: CustomTransactionError) -> u8 { + match variant { + CustomTransactionError::ColdkeyInSwapSchedule => 0, + } + } +} + #[freeze_struct("61e2b893d5ce6701")] #[derive(Encode, Decode, Clone, Eq, PartialEq, TypeInfo)] pub struct SubtensorSignedExtension(pub PhantomData); impl Default for SubtensorSignedExtension where - T::RuntimeCall: Dispatchable, + ::RuntimeCall: + Dispatchable, ::RuntimeCall: IsSubType>, { fn default() -> Self { @@ -1187,7 +1320,8 @@ where impl SubtensorSignedExtension where - T::RuntimeCall: Dispatchable, + ::RuntimeCall: + Dispatchable, ::RuntimeCall: IsSubType>, { pub fn new() -> Self { @@ -1218,14 +1352,15 @@ impl sp_std::fmt::Debug for SubtensorSignedE impl SignedExtension for SubtensorSignedExtension where - T::RuntimeCall: Dispatchable, + ::RuntimeCall: + Dispatchable, ::RuntimeCall: IsSubType>, ::RuntimeCall: IsSubType>, { const IDENTIFIER: &'static str = "SubtensorSignedExtension"; type AccountId = T::AccountId; - type Call = T::RuntimeCall; + type Call = ::RuntimeCall; type AdditionalSigned = (); type Pre = (CallType, u64, Self::AccountId); @@ -1250,7 +1385,7 @@ where ..Default::default() }) } else { - Err(InvalidTransaction::Call.into()) + Err(InvalidTransaction::Custom(1).into()) } } Some(Call::reveal_weights { netuid, .. }) => { @@ -1262,7 +1397,7 @@ where ..Default::default() }) } else { - Err(InvalidTransaction::Call.into()) + Err(InvalidTransaction::Custom(2).into()) } } Some(Call::set_weights { netuid, .. }) => { @@ -1274,7 +1409,7 @@ where ..Default::default() }) } else { - Err(InvalidTransaction::Call.into()) + Err(InvalidTransaction::Custom(3).into()) } } Some(Call::set_root_weights { netuid, hotkey, .. }) => { @@ -1286,7 +1421,7 @@ where ..Default::default() }) } else { - Err(InvalidTransaction::Call.into()) + Err(InvalidTransaction::Custom(4).into()) } } Some(Call::add_stake { .. }) => Ok(ValidTransaction { @@ -1305,7 +1440,7 @@ where if registrations_this_interval >= (max_registrations_per_interval.saturating_mul(3)) { // If the registration limit for the interval is exceeded, reject the transaction - return InvalidTransaction::ExhaustsResources.into(); + return Err(InvalidTransaction::Custom(5).into()); } Ok(ValidTransaction { priority: Self::get_priority_vanilla(), @@ -1316,14 +1451,36 @@ where priority: Self::get_priority_vanilla(), ..Default::default() }), - Some(Call::dissolve_network { .. }) => Ok(ValidTransaction { - priority: Self::get_priority_vanilla(), - ..Default::default() - }), - _ => Ok(ValidTransaction { - priority: Self::get_priority_vanilla(), - ..Default::default() - }), + Some(Call::dissolve_network { .. }) => { + if ColdkeySwapScheduled::::contains_key(who) { + InvalidTransaction::Custom(CustomTransactionError::ColdkeyInSwapSchedule.into()) + .into() + } else { + Ok(ValidTransaction { + priority: Self::get_priority_vanilla(), + ..Default::default() + }) + } + } + _ => { + if let Some( + BalancesCall::transfer_keep_alive { .. } + | BalancesCall::transfer_all { .. } + | BalancesCall::transfer_allow_death { .. }, + ) = call.is_sub_type() + { + if ColdkeySwapScheduled::::contains_key(who) { + return InvalidTransaction::Custom( + CustomTransactionError::ColdkeyInSwapSchedule.into(), + ) + .into(); + } + } + Ok(ValidTransaction { + priority: Self::get_priority_vanilla(), + ..Default::default() + }) + } } } diff --git a/pallets/subtensor/src/macros/config.rs b/pallets/subtensor/src/macros/config.rs index e59eac5ca..414af7e6b 100644 --- a/pallets/subtensor/src/macros/config.rs +++ b/pallets/subtensor/src/macros/config.rs @@ -1,15 +1,20 @@ #![allow(clippy::crate_in_macro_def)] use frame_support::pallet_macros::pallet_section; - /// A [`pallet_section`] that defines the errors for a pallet. /// This can later be imported into the pallet using [`import_section`]. #[pallet_section] mod config { - /// Configure the pallet by specifying the parameters and types on which it depends. #[pallet::config] pub trait Config: frame_system::Config { + /// call type + type RuntimeCall: Parameter + + Dispatchable + + From> + + IsType<::RuntimeCall> + + From>; + /// Because this pallet emits events, it depends on the runtime's definition of an event. type RuntimeEvent: From> + IsType<::RuntimeEvent>; @@ -31,6 +36,17 @@ mod config { /// Interface to allow other pallets to control who can register identities type TriumvirateInterface: crate::CollectiveInterface; + /// The scheduler type used for scheduling delayed calls. + type Scheduler: ScheduleAnon< + BlockNumberFor, + LocalCallOf, + PalletsOriginOf, + Hasher = Self::Hashing, + >; + + /// the preimage to store the call data. + type Preimages: QueryPreimage + StorePreimage; + /// ================================= /// ==== Initial Value Constants ==== /// ================================= @@ -112,10 +128,19 @@ mod config { type InitialMaxAllowedValidators: Get; /// Initial default delegation take. #[pallet::constant] - type InitialDefaultTake: Get; + type InitialDefaultDelegateTake: Get; /// Initial minimum delegation take. #[pallet::constant] - type InitialMinTake: Get; + type InitialMinDelegateTake: Get; + /// Initial default childkey take. + #[pallet::constant] + type InitialDefaultChildKeyTake: Get; + /// Initial minimum childkey take. + #[pallet::constant] + type InitialMinChildKeyTake: Get; + /// Initial maximum childkey take. + #[pallet::constant] + type InitialMaxChildKeyTake: Get; /// Initial weights version key. #[pallet::constant] type InitialWeightsVersionKey: Get; @@ -128,6 +153,9 @@ mod config { /// Initial delegate take transaction rate limit. #[pallet::constant] type InitialTxDelegateTakeRateLimit: Get; + /// Initial childkey take transaction rate limit. + #[pallet::constant] + type InitialTxChildKeyTakeRateLimit: Get; /// Initial percentage of total stake required to join senate. #[pallet::constant] type InitialSenateRequiredStakePercentage: Get; @@ -176,5 +204,11 @@ mod config { /// Initial hotkey emission tempo. #[pallet::constant] type InitialHotkeyEmissionTempo: Get; + /// Coldkey swap schedule duartion. + #[pallet::constant] + type InitialColdkeySwapScheduleDuration: Get>; + /// Dissolve network schedule duration + #[pallet::constant] + type InitialDissolveNetworkScheduleDuration: Get>; } } diff --git a/pallets/subtensor/src/macros/dispatches.rs b/pallets/subtensor/src/macros/dispatches.rs index 293dc0238..ce3f92879 100644 --- a/pallets/subtensor/src/macros/dispatches.rs +++ b/pallets/subtensor/src/macros/dispatches.rs @@ -4,6 +4,10 @@ use frame_support::pallet_macros::pallet_section; /// This can later be imported into the pallet using [`import_section`]. #[pallet_section] mod dispatches { + use frame_support::traits::schedule::v3::Anon as ScheduleAnon; + use frame_support::traits::schedule::DispatchTime; + use frame_system::pallet_prelude::BlockNumberFor; + use sp_runtime::traits::Saturating; /// Dispatchable functions allow users to interact with the pallet and invoke state changes. /// These functions materialize as "extrinsics", which are often compared to transactions. /// Dispatchable functions must be annotated with a weight and must return a DispatchResult. @@ -262,7 +266,7 @@ mod dispatches { .saturating_add(T::DbWeight::get().reads(6)) .saturating_add(T::DbWeight::get().writes(3)), DispatchClass::Normal, Pays::No))] pub fn become_delegate(origin: OriginFor, hotkey: T::AccountId) -> DispatchResult { - Self::do_become_delegate(origin, hotkey, Self::get_default_take()) + Self::do_become_delegate(origin, hotkey, Self::get_default_delegate_take()) } /// --- Allows delegates to decrease its take value. @@ -673,43 +677,141 @@ mod dispatches { .saturating_add(T::DbWeight::get().writes(527)), DispatchClass::Operational, Pays::No))] pub fn swap_coldkey( origin: OriginFor, + old_coldkey: T::AccountId, new_coldkey: T::AccountId, ) -> DispatchResultWithPostInfo { - Self::do_swap_coldkey(origin, &new_coldkey) + // Ensure it's called with root privileges (scheduler has root privileges) + ensure_root(origin.clone())?; + log::info!("swap_coldkey: {:?} -> {:?}", old_coldkey, new_coldkey); + + Self::do_swap_coldkey(&old_coldkey, &new_coldkey) } - /// Unstakes all tokens associated with a hotkey and transfers them to a new coldkey. + /// Sets the childkey take for a given hotkey. /// - /// # Arguments + /// This function allows a coldkey to set the childkey take for a given hotkey. + /// The childkey take determines the proportion of stake that the hotkey keeps for itself + /// when distributing stake to its children. /// - /// * `origin` - The origin of the call, must be signed by the current coldkey. - /// * `hotkey` - The hotkey associated with the stakes to be unstaked. - /// * `new_coldkey` - The new coldkey to receive the unstaked tokens. + /// # Arguments: + /// * `origin` (::RuntimeOrigin): + /// - The signature of the calling coldkey. Setting childkey take can only be done by the coldkey. /// - /// # Returns + /// * `hotkey` (T::AccountId): + /// - The hotkey for which the childkey take will be set. /// - /// Returns a `DispatchResult` indicating success or failure of the operation. + /// * `take` (u16): + /// - The new childkey take value. This is a percentage represented as a value between 0 and 10000, + /// where 10000 represents 100%. /// - /// # Weight + /// # Events: + /// * `ChildkeyTakeSet`: + /// - On successfully setting the childkey take for a hotkey. /// - /// Weight is calculated based on the number of database reads and writes. - #[cfg(test)] - #[pallet::call_index(72)] - #[pallet::weight((Weight::from_parts(21_000_000, 0) - .saturating_add(T::DbWeight::get().reads(3)) - .saturating_add(T::DbWeight::get().writes(3)), DispatchClass::Operational, Pays::No))] - pub fn schedule_coldkey_swap( - _origin: OriginFor, - _new_coldkey: T::AccountId, - _work: Vec, - _block_number: u64, - _nonce: u64, + /// # Errors: + /// * `NonAssociatedColdKey`: + /// - The coldkey does not own the hotkey. + /// * `InvalidChildkeyTake`: + /// - The provided take value is invalid (greater than the maximum allowed take). + /// * `TxChildkeyTakeRateLimitExceeded`: + /// - The rate limit for changing childkey take has been exceeded. + /// + #[pallet::call_index(75)] + #[pallet::weight(( + Weight::from_parts(34_000, 0) + .saturating_add(T::DbWeight::get().reads(4)) + .saturating_add(T::DbWeight::get().writes(2)), + DispatchClass::Normal, + Pays::Yes +))] + pub fn set_childkey_take( + origin: OriginFor, + hotkey: T::AccountId, + netuid: u16, + take: u16, ) -> DispatchResult { - Ok(()) + let coldkey = ensure_signed(origin)?; + + // Call the utility function to set the childkey take + Self::do_set_childkey_take(coldkey, hotkey, netuid, take) } // ---- SUDO ONLY FUNCTIONS ------------------------------------------------------------ + /// Sets the transaction rate limit for changing childkey take. + /// + /// This function can only be called by the root origin. + /// + /// # Arguments: + /// * `origin` - The origin of the call, must be root. + /// * `tx_rate_limit` - The new rate limit in blocks. + /// + /// # Errors: + /// * `BadOrigin` - If the origin is not root. + /// + #[pallet::call_index(69)] + #[pallet::weight(( + Weight::from_parts(6_000, 0) + .saturating_add(T::DbWeight::get().writes(1)), + DispatchClass::Operational, + Pays::No +))] + pub fn sudo_set_tx_childkey_take_rate_limit( + origin: OriginFor, + tx_rate_limit: u64, + ) -> DispatchResult { + ensure_root(origin)?; + Self::set_tx_childkey_take_rate_limit(tx_rate_limit); + Ok(()) + } + + /// Sets the minimum allowed childkey take. + /// + /// This function can only be called by the root origin. + /// + /// # Arguments: + /// * `origin` - The origin of the call, must be root. + /// * `take` - The new minimum childkey take value. + /// + /// # Errors: + /// * `BadOrigin` - If the origin is not root. + /// + #[pallet::call_index(76)] + #[pallet::weight(( + Weight::from_parts(6_000, 0) + .saturating_add(T::DbWeight::get().writes(1)), + DispatchClass::Operational, + Pays::No + ))] + pub fn sudo_set_min_childkey_take(origin: OriginFor, take: u16) -> DispatchResult { + ensure_root(origin)?; + Self::set_min_childkey_take(take); + Ok(()) + } + + /// Sets the maximum allowed childkey take. + /// + /// This function can only be called by the root origin. + /// + /// # Arguments: + /// * `origin` - The origin of the call, must be root. + /// * `take` - The new maximum childkey take value. + /// + /// # Errors: + /// * `BadOrigin` - If the origin is not root. + /// + #[pallet::call_index(77)] + #[pallet::weight(( + Weight::from_parts(6_000, 0) + .saturating_add(T::DbWeight::get().writes(1)), + DispatchClass::Operational, + Pays::No + ))] + pub fn sudo_set_max_childkey_take(origin: OriginFor, take: u16) -> DispatchResult { + ensure_root(origin)?; + Self::set_max_childkey_take(take); + Ok(()) + } // ================================== // ==== Parameter Sudo calls ======== // ================================== @@ -832,17 +934,6 @@ mod dispatches { Self::user_remove_network(origin, netuid) } - /// Sets values for liquid alpha - #[pallet::call_index(64)] - #[pallet::weight((0, DispatchClass::Operational, Pays::No))] - pub fn sudo_hotfix_swap_coldkey_delegates( - _origin: OriginFor, - _old_coldkey: T::AccountId, - _new_coldkey: T::AccountId, - ) -> DispatchResult { - Ok(()) - } - /// Set a single child for a given hotkey on a specified network. /// /// This function allows a coldkey to set a single child for a given hotkey on a specified network. @@ -901,5 +992,172 @@ mod dispatches { Self::do_set_children(origin, hotkey, netuid, children)?; Ok(().into()) } + + /// Schedules a coldkey swap operation to be executed at a future block. + /// + /// This function allows a user to schedule the swapping of their coldkey to a new one + /// at a specified future block. The swap is not executed immediately but is scheduled + /// to occur at the specified block number. + /// + /// # Arguments + /// + /// * `origin` - The origin of the call, which should be signed by the current coldkey owner. + /// * `new_coldkey` - The account ID of the new coldkey that will replace the current one. + /// * `when` - The block number at which the coldkey swap should be executed. + /// + /// # Returns + /// + /// Returns a `DispatchResultWithPostInfo` indicating whether the scheduling was successful. + /// + /// # Errors + /// + /// This function may return an error if: + /// * The origin is not signed. + /// * The scheduling fails due to conflicts or system constraints. + /// + /// # Notes + /// + /// - The actual swap is not performed by this function. It merely schedules the swap operation. + /// - The weight of this call is set to a fixed value and may need adjustment based on benchmarking. + /// + /// # TODO + /// + /// - Implement proper weight calculation based on the complexity of the operation. + /// - Consider adding checks to prevent scheduling too far into the future. + /// TODO: Benchmark this call + #[pallet::call_index(73)] + #[pallet::weight((Weight::from_parts(119_000_000, 0) + .saturating_add(T::DbWeight::get().reads(6)) + .saturating_add(T::DbWeight::get().writes(31)), DispatchClass::Operational, Pays::Yes))] + pub fn schedule_swap_coldkey( + origin: OriginFor, + new_coldkey: T::AccountId, + ) -> DispatchResultWithPostInfo { + let who = ensure_signed(origin)?; + ensure!( + !ColdkeySwapScheduled::::contains_key(&who), + Error::::SwapAlreadyScheduled + ); + + let current_block: BlockNumberFor = >::block_number(); + let duration: BlockNumberFor = ColdkeySwapScheduleDuration::::get(); + let when: BlockNumberFor = current_block.saturating_add(duration); + + let call = Call::::swap_coldkey { + old_coldkey: who.clone(), + new_coldkey: new_coldkey.clone(), + }; + + 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)?; + + ColdkeySwapScheduled::::insert(&who, ()); + // Emit the SwapScheduled event + Self::deposit_event(Event::ColdkeySwapScheduled { + old_coldkey: who.clone(), + new_coldkey: new_coldkey.clone(), + execution_block: when, + }); + + Ok(().into()) + } + + /// Schedule the dissolution of a network at a specified block number. + /// + /// # Arguments + /// + /// * `origin` - The origin of the call, must be signed by the sender. + /// * `netuid` - The u16 network identifier to be dissolved. + /// + /// # Returns + /// + /// Returns a `DispatchResultWithPostInfo` indicating success or failure of the operation. + /// + /// # Weight + /// + /// Weight is calculated based on the number of database reads and writes. + + #[pallet::call_index(74)] + #[pallet::weight((Weight::from_parts(119_000_000, 0) + .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, + ) -> 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 { 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::Signed(who.clone()).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. + /// # Args: + /// * 'origin': (Origin): + /// - The signature of the calling hotkey. + /// + /// * 'netuid' (u16): + /// - The u16 network identifier. + /// + /// * 'version' (u16): + /// - The bittensor version identifier. + /// + /// * 'ip' (u128): + /// - The prometheus ip information as a u128 encoded integer. + /// + /// * 'port' (u16): + /// - The prometheus port information as a u16 encoded integer. + /// + /// * 'ip_type' (u8): + /// - The ip type v4 or v6. + /// + #[pallet::call_index(68)] + #[pallet::weight((Weight::from_parts(45_000_000, 0) + .saturating_add(T::DbWeight::get().reads(4)) + .saturating_add(T::DbWeight::get().writes(1)), DispatchClass::Normal, Pays::Yes))] + pub fn set_identity( + origin: OriginFor, + name: Vec, + url: Vec, + image: Vec, + discord: Vec, + description: Vec, + additional: Vec, + ) -> DispatchResult { + Self::do_set_identity(origin, name, url, image, discord, description, additional) + } } } diff --git a/pallets/subtensor/src/macros/errors.rs b/pallets/subtensor/src/macros/errors.rs index 156cbea56..22a0a6f89 100644 --- a/pallets/subtensor/src/macros/errors.rs +++ b/pallets/subtensor/src/macros/errors.rs @@ -168,5 +168,21 @@ mod errors { TooManyChildren, /// Default transaction rate limit exceeded. TxRateLimitExceeded, + /// Swap coldkey only callable by root. + SwapColdkeyOnlyCallableByRoot, + /// Swap already scheduled. + SwapAlreadyScheduled, + /// failed to swap coldkey + FailedToSchedule, + /// New coldkey is hotkey + NewColdKeyIsHotkey, + /// New coldkey is in arbitration + NewColdkeyIsInArbitration, + /// Childkey take is invalid. + InvalidChildkeyTake, + /// Childkey take rate limit exceeded. + TxChildkeyTakeRateLimitExceeded, + /// Invalid identity. + InvalidIdentity, } } diff --git a/pallets/subtensor/src/macros/events.rs b/pallets/subtensor/src/macros/events.rs index b93b8296b..b5e34b842 100644 --- a/pallets/subtensor/src/macros/events.rs +++ b/pallets/subtensor/src/macros/events.rs @@ -83,6 +83,14 @@ mod events { TxRateLimitSet(u64), /// setting the delegate take transaction rate limit. TxDelegateTakeRateLimitSet(u64), + /// setting the childkey take transaction rate limit. + TxChildKeyTakeRateLimitSet(u64), + /// minimum childkey take set + MinChildKeyTakeSet(u16), + /// maximum childkey take set + MaxChildKeyTakeSet(u16), + /// childkey take set + ChildKeyTakeSet(T::AccountId, u16), /// a sudo call is done. Sudid(DispatchResult), /// registration is allowed/disallowed for a subnet. @@ -164,7 +172,7 @@ mod events { /// The account ID of the new coldkey new_coldkey: T::AccountId, /// The arbitration block for the coldkey swap - arbitration_block: u64, + execution_block: BlockNumberFor, }, /// The arbitration period has been extended ArbitrationPeriodExtended { @@ -177,5 +185,16 @@ mod events { HotkeyEmissionTempoSet(u64), /// The network maximum stake has been set NetworkMaxStakeSet(u16, u64), + /// The identity of a coldkey has been set + ChainIdentitySet(T::AccountId), + /// A dissolve network extrinsic scheduled. + DissolveNetworkScheduled { + /// The account ID schedule the dissolve network extrisnic + account: T::AccountId, + /// network ID will be dissolved + netuid: u16, + /// extrinsic execution block number + execution_block: BlockNumberFor, + }, } } diff --git a/pallets/subtensor/src/macros/hooks.rs b/pallets/subtensor/src/macros/hooks.rs index b04a29ff6..76f140002 100644 --- a/pallets/subtensor/src/macros/hooks.rs +++ b/pallets/subtensor/src/macros/hooks.rs @@ -19,7 +19,7 @@ mod hooks { match block_step_result { Ok(_) => { // --- If the block step was successful, return the weight. - log::info!("Successfully ran block step."); + log::debug!("Successfully ran block step."); Weight::from_parts(110_634_229_000_u64, 0) .saturating_add(T::DbWeight::get().reads(8304_u64)) .saturating_add(T::DbWeight::get().writes(110_u64)) @@ -68,8 +68,16 @@ mod hooks { .saturating_add(migrations::migrate_populate_staking_hotkeys::migrate_populate_staking_hotkeys::()) // Fix total coldkey stake. // Storage version v8 -> v9 - .saturating_add(migrations::migrate_fix_total_coldkey_stake::migrate_fix_total_coldkey_stake::()); + .saturating_add(migrations::migrate_fix_total_coldkey_stake::migrate_fix_total_coldkey_stake::()) + // Migrate Delegate Ids on chain + .saturating_add(migrations::migrate_chain_identity::migrate_set_hotkey_identities::()); weight } + + #[cfg(feature = "try-runtime")] + fn try_state(_n: BlockNumberFor) -> Result<(), sp_runtime::TryRuntimeError> { + Self::check_accounting_invariants()?; + Ok(()) + } } } diff --git a/pallets/subtensor/src/migrations/migrate_chain_identity.rs b/pallets/subtensor/src/migrations/migrate_chain_identity.rs new file mode 100644 index 000000000..06ee5dd3f --- /dev/null +++ b/pallets/subtensor/src/migrations/migrate_chain_identity.rs @@ -0,0 +1,171 @@ +use crate::alloc::borrow::ToOwned; +use codec::Decode; +use scale_info::prelude::{string::String, vec::Vec}; +use serde::Deserialize; +use sp_core::{crypto::Ss58Codec, ConstU32}; +use sp_runtime::{AccountId32, BoundedVec}; + +use super::*; +use frame_support::{traits::Get, weights::Weight}; +use log; + +#[derive(Deserialize, Debug)] +struct RegistrationRecordJSON { + address: String, + name: String, + url: String, + description: String, +} + +fn string_to_bounded_vec(input: &str) -> Result>, &'static str> { + let vec_u8: Vec = input.to_owned().into_bytes(); + + // Check if the length is within bounds + if vec_u8.len() > 64 { + return Err("Input string is too long"); + } + + // Convert to BoundedVec + BoundedVec::>::try_from(vec_u8) + .map_err(|_| "Failed to convert to BoundedVec") +} + +pub fn migrate_set_hotkey_identities() -> Weight { + let migration_name = b"migrate_identities".to_vec(); + + // 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) + ); + + // Include the JSON file with delegate info + let data = include_str!("../../../../docs/delegate-info.json"); + + // Iterate over all the delegate records + if let Ok(delegates) = serde_json::from_str::>(data) { + // Iterate through the delegates + for delegate in delegates.iter() { + // Convert fields to bounded vecs + let name_result = string_to_bounded_vec(&delegate.name); + let desc_result = string_to_bounded_vec(&delegate.description); + let url_result = string_to_bounded_vec(&delegate.url); + let hotkey: AccountId32 = match AccountId32::from_ss58check(&delegate.address) { + Ok(account) => account, + Err(_) => { + log::warn!( + "Invalid SS58 address: {:?}. Skipping this delegate.", + delegate.address + ); + continue; + } + }; + let decoded_hotkey: T::AccountId = match T::AccountId::decode(&mut hotkey.as_ref()) { + Ok(decoded) => decoded, + Err(e) => { + log::warn!("Failed to decode hotkey: {:?}. Skipping this delegate.", e); + continue; + } + }; + log::info!("Hotkey unwrapped: {:?}", decoded_hotkey); + + // If we should continue with real values. + let mut name: BoundedVec> = BoundedVec::default(); + let mut description: BoundedVec> = BoundedVec::default(); + let mut url: BoundedVec> = BoundedVec::default(); + if let Ok(n) = name_result { + name = n; + } + if let Ok(d) = desc_result { + description = d; + } + if let Ok(u) = url_result { + url = u; + } + + // Unwrap the real values. + let image: BoundedVec> = BoundedVec::default(); + let discord: BoundedVec> = BoundedVec::default(); + let additional: BoundedVec> = BoundedVec::default(); + + // Create the chain identity. + let identity = ChainIdentityOf { + name: name.into(), + url: url.into(), + image: image.into(), + discord: discord.into(), + description: description.into(), + additional: additional.into(), + }; + + // Log the identity details + log::info!("Setting identity for hotkey: {:?}", hotkey); + log::info!("Name: {:?}", String::from_utf8_lossy(&identity.name)); + log::info!("URL: {:?}", String::from_utf8_lossy(&identity.url)); + log::info!("Image: {:?}", String::from_utf8_lossy(&identity.image)); + log::info!("Discord: {:?}", String::from_utf8_lossy(&identity.discord)); + log::info!( + "Description: {:?}", + String::from_utf8_lossy(&identity.description) + ); + log::info!( + "Additional: {:?}", + String::from_utf8_lossy(&identity.additional) + ); + + // Check validation. + let total_length = identity + .name + .len() + .saturating_add(identity.url.len()) + .saturating_add(identity.image.len()) + .saturating_add(identity.discord.len()) + .saturating_add(identity.description.len()) + .saturating_add(identity.additional.len()); + let is_valid: bool = total_length <= 256 + 256 + 1024 + 256 + 1024 + 1024 + && identity.name.len() <= 256 + && identity.url.len() <= 256 + && identity.image.len() <= 1024 + && identity.discord.len() <= 256 + && identity.description.len() <= 1024 + && identity.additional.len() <= 1024; + if !is_valid { + log::info!("Bytes not correct"); + continue; + } + + // Get the owning coldkey. + let coldkey = Owner::::get(decoded_hotkey.clone()); + log::info!("ColdKey: {:?}", decoded_hotkey); + + weight = weight.saturating_add(T::DbWeight::get().reads(1)); + + // Sink into the map. + Identities::::insert(coldkey.clone(), identity.clone()); + weight = weight.saturating_add(T::DbWeight::get().writes(1)); + } + } else { + log::info!("Failed to decode JSON"); + } + // Mark the migration as completed + HasMigrationRun::::insert(&migration_name, true); + weight = weight.saturating_add(T::DbWeight::get().writes(1)); + + log::info!( + "Migration '{:?}' completed. Storage version set to 7.", + String::from_utf8_lossy(&migration_name) + ); + + // Return the migration weight. + weight +} diff --git a/pallets/subtensor/src/migrations/migrate_init_total_issuance.rs b/pallets/subtensor/src/migrations/migrate_init_total_issuance.rs new file mode 100644 index 000000000..a488771c5 --- /dev/null +++ b/pallets/subtensor/src/migrations/migrate_init_total_issuance.rs @@ -0,0 +1,83 @@ +use super::*; +use frame_support::pallet_prelude::OptionQuery; +use frame_support::{pallet_prelude::Identity, storage_alias}; +use sp_std::vec::Vec; + +// TODO: Implement comprehensive tests for this migration + +/// Module containing deprecated storage format for LoadedEmission +pub mod deprecated_loaded_emission_format { + use super::*; + + #[storage_alias] + pub(super) type LoadedEmission = + StorageMap, Identity, u16, Vec<(AccountIdOf, u64)>, OptionQuery>; +} + +pub mod initialise_total_issuance { + use frame_support::pallet_prelude::Weight; + use frame_support::traits::{fungible, OnRuntimeUpgrade}; + use sp_core::Get; + + use crate::*; + + pub struct Migration(PhantomData); + + impl OnRuntimeUpgrade for Migration { + /// Performs the migration to initialize and update the total issuance. + /// + /// This function does the following: + /// 1. Calculates the total locked tokens across all subnets + /// 2. Retrieves the total account balances and total stake + /// 3. Computes and updates the new total issuance + /// + /// Returns the weight of the migration operation. + fn on_runtime_upgrade() -> Weight { + // Calculate the total locked tokens across all subnets + let subnets_len = crate::SubnetLocked::::iter().count() as u64; + let total_subnet_locked: u64 = + crate::SubnetLocked::::iter().fold(0, |acc, (_, v)| acc.saturating_add(v)); + + // Retrieve the total balance of all accounts + let total_account_balances = <::Currency as fungible::Inspect< + ::AccountId, + >>::total_issuance(); + + // Get the total stake from the system + let total_stake = crate::TotalStake::::get(); + + // Retrieve the previous total issuance for logging purposes + let prev_total_issuance = crate::TotalIssuance::::get(); + + // Calculate the new total issuance + let new_total_issuance = total_account_balances + .saturating_add(total_stake) + .saturating_add(total_subnet_locked); + + // Update the total issuance in storage + crate::TotalIssuance::::put(new_total_issuance); + + // Log the change in total issuance + log::info!( + "Subtensor Pallet Total Issuance Updated: previous: {:?}, new: {:?}", + prev_total_issuance, + new_total_issuance + ); + + // Return the weight of the operation + // We performed subnets_len + 5 reads and 1 write + ::DbWeight::get() + .reads_writes(subnets_len.saturating_add(5), 1) + } + + /// Performs post-upgrade checks to ensure the migration was successful. + /// + /// This function is only compiled when the "try-runtime" feature is enabled. + #[cfg(feature = "try-runtime")] + fn post_upgrade(_state: Vec) -> Result<(), sp_runtime::TryRuntimeError> { + // Verify that all accounting invariants are satisfied after the migration + crate::Pallet::::check_accounting_invariants()?; + Ok(()) + } + } +} diff --git a/pallets/subtensor/src/migrations/mod.rs b/pallets/subtensor/src/migrations/mod.rs index df0f7ce06..6036b23e0 100644 --- a/pallets/subtensor/src/migrations/mod.rs +++ b/pallets/subtensor/src/migrations/mod.rs @@ -1,8 +1,10 @@ use super::*; +pub mod migrate_chain_identity; pub mod migrate_create_root_network; pub mod migrate_delete_subnet_21; pub mod migrate_delete_subnet_3; pub mod migrate_fix_total_coldkey_stake; +pub mod migrate_init_total_issuance; pub mod migrate_populate_owned_hotkeys; pub mod migrate_populate_staking_hotkeys; pub mod migrate_to_v1_separate_emission; diff --git a/pallets/subtensor/src/rpc_info/delegate_info.rs b/pallets/subtensor/src/rpc_info/delegate_info.rs index 56b25d230..a41b6e17e 100644 --- a/pallets/subtensor/src/rpc_info/delegate_info.rs +++ b/pallets/subtensor/src/rpc_info/delegate_info.rs @@ -148,7 +148,7 @@ impl Pallet { } } - log::info!( + log::debug!( "Total delegated stake for coldkey {:?}: {}", coldkey, total_delegated diff --git a/pallets/subtensor/src/staking/add_stake.rs b/pallets/subtensor/src/staking/add_stake.rs index f62fd7cd2..c9cbd7e04 100644 --- a/pallets/subtensor/src/staking/add_stake.rs +++ b/pallets/subtensor/src/staking/add_stake.rs @@ -37,7 +37,7 @@ impl Pallet { ) -> dispatch::DispatchResult { // We check that the transaction is signed by the caller and retrieve the T::AccountId coldkey information. let coldkey = ensure_signed(origin)?; - log::info!( + log::debug!( "do_add_stake( origin:{:?} hotkey:{:?}, stake_to_be_added:{:?} )", coldkey, hotkey, @@ -102,7 +102,7 @@ impl Pallet { stakes_this_interval.saturating_add(1), block, ); - log::info!( + log::debug!( "StakeAdded( hotkey:{:?}, stake_to_be_added:{:?} )", hotkey, actual_amount_to_stake diff --git a/pallets/subtensor/src/staking/become_delegate.rs b/pallets/subtensor/src/staking/become_delegate.rs index 064f47c12..fd600453f 100644 --- a/pallets/subtensor/src/staking/become_delegate.rs +++ b/pallets/subtensor/src/staking/become_delegate.rs @@ -34,7 +34,7 @@ impl Pallet { ) -> dispatch::DispatchResult { // --- 1. We check the coldkey signuture. let coldkey = ensure_signed(origin)?; - log::info!( + log::debug!( "do_become_delegate( origin:{:?} hotkey:{:?}, take:{:?} )", coldkey, hotkey, @@ -58,9 +58,9 @@ impl Pallet { Error::::DelegateTxRateLimitExceeded ); - // --- 5.1 Ensure take is within the min ..= InitialDefaultTake (18%) range - let min_take = MinTake::::get(); - let max_take = MaxTake::::get(); + // --- 5.1 Ensure take is within the min ..= InitialDefaultDelegateTake (18%) range + let min_take = MinDelegateTake::::get(); + let max_take = MaxDelegateTake::::get(); ensure!(take >= min_take, Error::::DelegateTakeTooLow); ensure!(take <= max_take, Error::::DelegateTakeTooHigh); @@ -72,7 +72,7 @@ impl Pallet { Self::set_last_tx_block_delegate_take(&coldkey, block); // --- 7. Emit the staking event. - log::info!( + log::debug!( "DelegateAdded( coldkey:{:?}, hotkey:{:?}, take:{:?} )", coldkey, hotkey, diff --git a/pallets/subtensor/src/staking/decrease_take.rs b/pallets/subtensor/src/staking/decrease_take.rs index 9e48bac91..8742f809d 100644 --- a/pallets/subtensor/src/staking/decrease_take.rs +++ b/pallets/subtensor/src/staking/decrease_take.rs @@ -34,7 +34,7 @@ impl Pallet { ) -> dispatch::DispatchResult { // --- 1. We check the coldkey signature. let coldkey = ensure_signed(origin)?; - log::info!( + log::debug!( "do_decrease_take( origin:{:?} hotkey:{:?}, take:{:?} )", coldkey, hotkey, @@ -50,15 +50,15 @@ impl Pallet { ensure!(take < current_take, Error::::DelegateTakeTooLow); } - // --- 3.1 Ensure take is within the min ..= InitialDefaultTake (18%) range - let min_take = MinTake::::get(); + // --- 3.1 Ensure take is within the min ..= InitialDefaultDelegateTake (18%) range + let min_take = MinDelegateTake::::get(); ensure!(take >= min_take, Error::::DelegateTakeTooLow); // --- 4. Set the new take value. Delegates::::insert(hotkey.clone(), take); // --- 5. Emit the take value. - log::info!( + log::debug!( "TakeDecreased( coldkey:{:?}, hotkey:{:?}, take:{:?} )", coldkey, hotkey, diff --git a/pallets/subtensor/src/staking/helpers.rs b/pallets/subtensor/src/staking/helpers.rs index 486577712..0328d94e6 100644 --- a/pallets/subtensor/src/staking/helpers.rs +++ b/pallets/subtensor/src/staking/helpers.rs @@ -218,6 +218,13 @@ impl Pallet { hotkey: &T::AccountId, increment: u64, ) { + log::debug!( + "Increasing stake: coldkey: {:?}, hotkey: {:?}, amount: {}", + coldkey, + hotkey, + increment + ); + TotalColdkeyStake::::insert( coldkey, TotalColdkeyStake::::get(coldkey).saturating_add(increment), @@ -284,7 +291,6 @@ impl Pallet { 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)); // Update StakingHotkeys map let mut staking_hotkeys = StakingHotkeys::::get(coldkey); diff --git a/pallets/subtensor/src/staking/increase_take.rs b/pallets/subtensor/src/staking/increase_take.rs index aa6dd443c..021818447 100644 --- a/pallets/subtensor/src/staking/increase_take.rs +++ b/pallets/subtensor/src/staking/increase_take.rs @@ -37,7 +37,7 @@ impl Pallet { ) -> dispatch::DispatchResult { // --- 1. We check the coldkey signature. let coldkey = ensure_signed(origin)?; - log::info!( + log::debug!( "do_increase_take( origin:{:?} hotkey:{:?}, take:{:?} )", coldkey, hotkey, @@ -53,8 +53,8 @@ impl Pallet { ensure!(take > current_take, Error::::DelegateTakeTooLow); } - // --- 4. Ensure take is within the min ..= InitialDefaultTake (18%) range - let max_take = MaxTake::::get(); + // --- 4. Ensure take is within the min ..= InitialDefaultDelegateTake (18%) range + let max_take = MaxDelegateTake::::get(); ensure!(take <= max_take, Error::::DelegateTakeTooHigh); // --- 5. Enforce the rate limit (independently on do_add_stake rate limits) @@ -74,7 +74,7 @@ impl Pallet { Delegates::::insert(hotkey.clone(), take); // --- 7. Emit the take value. - log::info!( + log::debug!( "TakeIncreased( coldkey:{:?}, hotkey:{:?}, take:{:?} )", coldkey, hotkey, diff --git a/pallets/subtensor/src/staking/remove_stake.rs b/pallets/subtensor/src/staking/remove_stake.rs index a6f3db08d..4118e8d07 100644 --- a/pallets/subtensor/src/staking/remove_stake.rs +++ b/pallets/subtensor/src/staking/remove_stake.rs @@ -37,7 +37,7 @@ impl Pallet { ) -> dispatch::DispatchResult { // 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:{:?}, stake_to_be_removed:{:?} )", coldkey, hotkey, @@ -96,7 +96,7 @@ impl Pallet { unstakes_this_interval.saturating_add(1), block, ); - log::info!( + log::debug!( "StakeRemoved( hotkey:{:?}, stake_to_be_removed:{:?} )", hotkey, stake_to_be_removed diff --git a/pallets/subtensor/src/staking/set_children.rs b/pallets/subtensor/src/staking/set_children.rs index f413db23f..9029d58ca 100644 --- a/pallets/subtensor/src/staking/set_children.rs +++ b/pallets/subtensor/src/staking/set_children.rs @@ -60,13 +60,23 @@ impl Pallet { // Ensure the hotkey passes the rate limit. ensure!( - Self::passes_rate_limit_globally( + Self::passes_rate_limit_on_subnet( &TransactionType::SetChildren, // Set children. &hotkey, // Specific to a hotkey. + netuid, // Specific to a subnet. ), Error::::TxRateLimitExceeded ); + // Set last transaction block + let current_block = Self::get_current_block_as_u64(); + Self::set_last_transaction_block( + &hotkey, + netuid, + &TransactionType::SetChildren, + current_block, + ); + // --- 2. Check that this delegation is not on the root network. Child hotkeys are not valid on root. ensure!( netuid != Self::get_root_netuid(), @@ -190,4 +200,105 @@ impl Pallet { pub fn get_parents(child: &T::AccountId, netuid: u16) -> Vec<(u64, T::AccountId)> { ParentKeys::::get(child, netuid) } + + /// Sets the childkey take for a given hotkey. + /// + /// This function allows a coldkey to set the childkey take for a given hotkey. + /// The childkey take determines the proportion of stake that the hotkey keeps for itself + /// when distributing stake to its children. + /// + /// # Arguments: + /// * `coldkey` (T::AccountId): + /// - The coldkey that owns the hotkey. + /// + /// * `hotkey` (T::AccountId): + /// - The hotkey for which the childkey take will be set. + /// + /// * `take` (u16): + /// - The new childkey take value. This is a percentage represented as a value between 0 and 10000, + /// where 10000 represents 100%. + /// + /// # Returns: + /// * `DispatchResult` - The result of the operation. + /// + /// # Errors: + /// * `NonAssociatedColdKey`: + /// - The coldkey does not own the hotkey. + /// * `InvalidChildkeyTake`: + /// - The provided take value is invalid (greater than the maximum allowed take). + /// * `TxChildkeyTakeRateLimitExceeded`: + /// - The rate limit for changing childkey take has been exceeded. + pub fn do_set_childkey_take( + coldkey: T::AccountId, + hotkey: T::AccountId, + netuid: u16, + take: u16, + ) -> DispatchResult { + // Ensure the coldkey owns the hotkey + ensure!( + Self::coldkey_owns_hotkey(&coldkey, &hotkey), + Error::::NonAssociatedColdKey + ); + + // Ensure the take value is valid + ensure!( + take <= Self::get_max_childkey_take(), + Error::::InvalidChildkeyTake + ); + + // Ensure the hotkey passes the rate limit. + ensure!( + Self::passes_rate_limit_on_subnet( + &TransactionType::SetChildkeyTake, // Set childkey take. + &hotkey, // Specific to a hotkey. + netuid, // Specific to a subnet. + ), + Error::::TxChildkeyTakeRateLimitExceeded + ); + + // Set last transaction block + let current_block = Self::get_current_block_as_u64(); + Self::set_last_transaction_block( + &hotkey, + netuid, + &TransactionType::SetChildkeyTake, + current_block, + ); + + // Set the new childkey take value for the given hotkey and network + ChildkeyTake::::insert(hotkey.clone(), netuid, take); + + // Update the last transaction block + Self::set_last_transaction_block( + &hotkey, + netuid, + &TransactionType::SetChildkeyTake, + current_block, + ); + + // Emit the event + Self::deposit_event(Event::ChildKeyTakeSet(hotkey.clone(), take)); + log::debug!( + "Childkey take set for hotkey: {:?} and take: {:?}", + hotkey, + take + ); + Ok(()) + } + + /// Gets the childkey take for a given hotkey. + /// + /// This function retrieves the current childkey take value for a specified hotkey. + /// If no specific take value has been set, it returns the default childkey take. + /// + /// # Arguments: + /// * `hotkey` (&T::AccountId): + /// - The hotkey for which to retrieve the childkey take. + /// + /// # Returns: + /// * `u16` - The childkey take value. This is a percentage represented as a value between 0 and 10000, + /// where 10000 represents 100%. + pub fn get_childkey_take(hotkey: &T::AccountId, netuid: u16) -> u16 { + ChildkeyTake::::get(hotkey, netuid) + } } diff --git a/pallets/subtensor/src/subnets/registration.rs b/pallets/subtensor/src/subnets/registration.rs index 9319adcd1..9310b7a3f 100644 --- a/pallets/subtensor/src/subnets/registration.rs +++ b/pallets/subtensor/src/subnets/registration.rs @@ -41,7 +41,7 @@ impl Pallet { ) -> DispatchResult { // --- 1. Check that the caller has signed the transaction. (the coldkey of the pairing) let coldkey = ensure_signed(origin)?; - log::info!( + log::debug!( "do_registration( coldkey:{:?} netuid:{:?} hotkey:{:?} )", coldkey, netuid, @@ -131,7 +131,7 @@ impl Pallet { // --- 12.1.2 Expand subnetwork with new account. Self::append_neuron(netuid, &hotkey, current_block_number); - log::info!("add new neuron account"); + log::debug!("add new neuron account"); } else { // --- 13.1.1 Replacement required. // We take the neuron with the lowest pruning score here. @@ -139,7 +139,7 @@ impl Pallet { // --- 13.1.1 Replace the neuron account with the new info. Self::replace_neuron(netuid, subnetwork_uid, &hotkey, current_block_number); - log::info!("prune neuron"); + log::debug!("prune neuron"); } // --- 14. Record the registration and increment block and interval counters. @@ -149,7 +149,7 @@ impl Pallet { Self::increase_rao_recycled(netuid, Self::get_burn_as_u64(netuid)); // --- 15. Deposit successful event. - log::info!( + log::debug!( "NeuronRegistered( netuid:{:?} uid:{:?} hotkey:{:?} ) ", netuid, subnetwork_uid, @@ -220,7 +220,7 @@ impl Pallet { // --- 1. Check that the caller has signed the transaction. // TODO( const ): This not be the hotkey signature or else an exterior actor can register the hotkey and potentially control it? let signing_origin = ensure_signed(origin)?; - log::info!( + log::debug!( "do_registration( origin:{:?} netuid:{:?} hotkey:{:?}, coldkey:{:?} )", signing_origin, netuid, @@ -326,7 +326,7 @@ impl Pallet { // --- 11.1.2 Expand subnetwork with new account. Self::append_neuron(netuid, &hotkey, current_block_number); - log::info!("add new neuron account"); + log::debug!("add new neuron account"); } else { // --- 11.1.1 Replacement required. // We take the neuron with the lowest pruning score here. @@ -334,7 +334,7 @@ impl Pallet { // --- 11.1.1 Replace the neuron account with the new info. Self::replace_neuron(netuid, subnetwork_uid, &hotkey, current_block_number); - log::info!("prune neuron"); + log::debug!("prune neuron"); } // --- 12. Record the registration and increment block and interval counters. @@ -343,7 +343,7 @@ impl Pallet { RegistrationsThisBlock::::mutate(netuid, |val| val.saturating_inc()); // --- 13. Deposit successful event. - log::info!( + log::debug!( "NeuronRegistered( netuid:{:?} uid:{:?} hotkey:{:?} ) ", netuid, subnetwork_uid, @@ -366,7 +366,7 @@ impl Pallet { // --- 1. Check that the caller has signed the transaction. let coldkey = ensure_signed(origin)?; - log::info!("do_faucet( coldkey:{:?} )", coldkey); + log::debug!("do_faucet( coldkey:{:?} )", coldkey); // --- 2. Ensure the passed block number is valid, not in the future or too old. // Work must have been done within 3 blocks (stops long range attacks). @@ -400,7 +400,7 @@ impl Pallet { Self::add_balance_to_coldkey_account(&coldkey, balance_to_add); // --- 6. Deposit successful event. - log::info!( + log::debug!( "Faucet( coldkey:{:?} amount:{:?} ) ", coldkey, balance_to_add @@ -419,65 +419,67 @@ impl Pallet { } /// Determine which peer to prune from the network by finding the element with the lowest pruning score out of - /// immunity period. If all neurons are in immunity period, return node with lowest prunning score. - /// This function will always return an element to prune. + /// immunity period. If there is a tie for lowest pruning score, the neuron registered earliest is pruned. + /// If all neurons are in immunity period, the neuron with the lowest pruning score is pruned. If there is a tie for + /// the lowest pruning score, the immune neuron registered earliest is pruned. + /// Ties for earliest registration are broken by the neuron with the lowest uid. pub fn get_neuron_to_prune(netuid: u16) -> u16 { let mut min_score: u16 = u16::MAX; - let mut min_score_in_immunity_period = u16::MAX; - let mut uid_with_min_score = 0; - let mut uid_with_min_score_in_immunity_period: u16 = 0; + let mut min_score_in_immunity: u16 = u16::MAX; + let mut earliest_registration: u64 = u64::MAX; + let mut earliest_registration_in_immunity: u64 = u64::MAX; + let mut uid_to_prune: u16 = 0; + let mut uid_to_prune_in_immunity: u16 = 0; + + // This boolean is used instead of checking if min_score == u16::MAX, to avoid the case + // where all non-immune neurons have pruning score u16::MAX + // This may be unlikely in practice. + let mut found_non_immune = false; let neurons_n = Self::get_subnetwork_n(netuid); if neurons_n == 0 { return 0; // If there are no neurons in this network. } - let current_block: u64 = Self::get_current_block_as_u64(); - let immunity_period: u64 = Self::get_immunity_period(netuid) as u64; - for neuron_uid_i in 0..neurons_n { - let pruning_score: u16 = Self::get_pruning_score_for_uid(netuid, neuron_uid_i); + for neuron_uid in 0..neurons_n { + let pruning_score: u16 = Self::get_pruning_score_for_uid(netuid, neuron_uid); let block_at_registration: u64 = - Self::get_neuron_block_at_registration(netuid, neuron_uid_i); - #[allow(clippy::comparison_chain)] - if min_score == pruning_score { - if current_block.saturating_sub(block_at_registration) < immunity_period { - //neuron is in immunity period - if min_score_in_immunity_period > pruning_score { - min_score_in_immunity_period = pruning_score; - uid_with_min_score_in_immunity_period = neuron_uid_i; - } - } else { - uid_with_min_score = neuron_uid_i; + Self::get_neuron_block_at_registration(netuid, neuron_uid); + let is_immune = Self::get_neuron_is_immune(netuid, neuron_uid); + + if is_immune { + // if the immune neuron has a lower pruning score than the minimum for immune neurons, + // or, if the pruning scores are equal and the immune neuron was registered earlier than the current minimum for immune neurons, + // then update the minimum pruning score and the uid to prune for immune neurons + if pruning_score < min_score_in_immunity + || (pruning_score == min_score_in_immunity + && block_at_registration < earliest_registration_in_immunity) + { + min_score_in_immunity = pruning_score; + earliest_registration_in_immunity = block_at_registration; + uid_to_prune_in_immunity = neuron_uid; } - } - // Find min pruning score. - else if min_score > pruning_score { - if current_block.saturating_sub(block_at_registration) < immunity_period { - //neuron is in immunity period - if min_score_in_immunity_period > pruning_score { - min_score_in_immunity_period = pruning_score; - uid_with_min_score_in_immunity_period = neuron_uid_i; - } - } else { + } else { + found_non_immune = true; + // if the non-immune neuron has a lower pruning score than the minimum for non-immune neurons, + // or, if the pruning scores are equal and the non-immune neuron was registered earlier than the current minimum for non-immune neurons, + // then update the minimum pruning score and the uid to prune for non-immune neurons + if pruning_score < min_score + || (pruning_score == min_score && block_at_registration < earliest_registration) + { min_score = pruning_score; - uid_with_min_score = neuron_uid_i; + earliest_registration = block_at_registration; + uid_to_prune = neuron_uid; } } } - if min_score == u16::MAX { - //all neuorns are in immunity period - Self::set_pruning_score_for_uid( - netuid, - uid_with_min_score_in_immunity_period, - u16::MAX, - ); - uid_with_min_score_in_immunity_period + + if found_non_immune { + Self::set_pruning_score_for_uid(netuid, uid_to_prune, u16::MAX); + uid_to_prune } else { - // We replace the pruning score here with u16 max to ensure that all peers always have a - // pruning score. In the event that every peer has been pruned this function will prune - // the last element in the network continually. - Self::set_pruning_score_for_uid(netuid, uid_with_min_score, u16::MAX); - uid_with_min_score + Self::set_pruning_score_for_uid(netuid, uid_to_prune_in_immunity, u16::MAX); + uid_to_prune_in_immunity } } diff --git a/pallets/subtensor/src/subnets/serving.rs b/pallets/subtensor/src/subnets/serving.rs index eb7fa4369..1a9240c36 100644 --- a/pallets/subtensor/src/subnets/serving.rs +++ b/pallets/subtensor/src/subnets/serving.rs @@ -106,7 +106,7 @@ impl Pallet { Axons::::insert(netuid, hotkey_id.clone(), prev_axon); // We deposit axon served event. - log::info!("AxonServed( hotkey:{:?} ) ", hotkey_id.clone()); + log::debug!("AxonServed( hotkey:{:?} ) ", hotkey_id.clone()); Self::deposit_event(Event::AxonServed(netuid, hotkey_id)); // Return is successful dispatch. @@ -204,7 +204,7 @@ impl Pallet { Prometheus::::insert(netuid, hotkey_id.clone(), prev_prometheus); // We deposit prometheus served event. - log::info!("PrometheusServed( hotkey:{:?} ) ", hotkey_id.clone()); + log::debug!("PrometheusServed( hotkey:{:?} ) ", hotkey_id.clone()); Self::deposit_event(Event::PrometheusServed(netuid, hotkey_id)); // Return is successful dispatch. diff --git a/pallets/subtensor/src/subnets/weights.rs b/pallets/subtensor/src/subnets/weights.rs index 67b1d485e..1a53e44cc 100644 --- a/pallets/subtensor/src/subnets/weights.rs +++ b/pallets/subtensor/src/subnets/weights.rs @@ -28,7 +28,7 @@ impl Pallet { ) -> DispatchResult { let who = ensure_signed(origin)?; - log::info!("do_commit_weights( hotkey:{:?} netuid:{:?})", who, netuid); + log::debug!("do_commit_weights( hotkey:{:?} netuid:{:?})", who, netuid); ensure!( Self::get_commit_reveal_weights_enabled(netuid), @@ -89,7 +89,7 @@ impl Pallet { ) -> DispatchResult { let who = ensure_signed(origin.clone())?; - log::info!("do_reveal_weights( hotkey:{:?} netuid:{:?})", who, netuid); + log::debug!("do_reveal_weights( hotkey:{:?} netuid:{:?})", who, netuid); ensure!( Self::get_commit_reveal_weights_enabled(netuid), @@ -188,7 +188,7 @@ impl Pallet { ) -> dispatch::DispatchResult { // --- 1. Check the caller's signature. This is the hotkey of a registered account. let hotkey = ensure_signed(origin)?; - log::info!( + log::debug!( "do_set_weights( origin:{:?} netuid:{:?}, uids:{:?}, values:{:?})", hotkey, netuid, @@ -289,7 +289,7 @@ impl Pallet { Self::set_last_update_for_uid(netuid, neuron_uid, current_block); // --- 19. Emit the tracking event. - log::info!( + log::debug!( "WeightsSet( netuid:{:?}, neuron_uid:{:?} )", netuid, neuron_uid @@ -308,7 +308,7 @@ impl Pallet { /// pub fn check_version_key(netuid: u16, version_key: u64) -> bool { let network_version_key: u64 = WeightsVersionKey::::get(netuid); - log::info!( + log::debug!( "check_version_key( network_version_key:{:?}, version_key:{:?} )", network_version_key, version_key diff --git a/pallets/subtensor/src/swap/swap_coldkey.rs b/pallets/subtensor/src/swap/swap_coldkey.rs index bd8a11fa1..498549e5c 100644 --- a/pallets/subtensor/src/swap/swap_coldkey.rs +++ b/pallets/subtensor/src/swap/swap_coldkey.rs @@ -30,51 +30,50 @@ impl Pallet { /// /// Weight is tracked and updated throughout the function execution. pub fn do_swap_coldkey( - origin: T::RuntimeOrigin, + old_coldkey: &T::AccountId, new_coldkey: &T::AccountId, ) -> DispatchResultWithPostInfo { - // 1. Ensure the origin is signed and get the old coldkey - let old_coldkey = ensure_signed(origin)?; - // 2. Initialize the weight for this operation let mut weight: Weight = T::DbWeight::get().reads(2); - // 3. Ensure the new coldkey is not associated with any hotkeys ensure!( StakingHotkeys::::get(new_coldkey).is_empty(), Error::::ColdKeyAlreadyAssociated ); + weight = weight.saturating_add(T::DbWeight::get().reads(1)); // 4. Ensure the new coldkey is not a hotkey ensure!( !Self::hotkey_account_exists(new_coldkey), - Error::::ColdKeyAlreadyAssociated + Error::::NewColdKeyIsHotkey ); + weight = weight.saturating_add(T::DbWeight::get().reads(1)); // 5. Calculate the swap cost and ensure sufficient balance let swap_cost = Self::get_key_swap_cost(); - log::debug!("Coldkey swap cost: {:?}", swap_cost); ensure!( - Self::can_remove_balance_from_coldkey_account(&old_coldkey, swap_cost), + Self::can_remove_balance_from_coldkey_account(old_coldkey, swap_cost), Error::::NotEnoughBalanceToPaySwapColdKey ); // 6. Remove and burn the swap cost from the old coldkey's account - let actual_burn_amount = - Self::remove_balance_from_coldkey_account(&old_coldkey, swap_cost)?; + let actual_burn_amount = Self::remove_balance_from_coldkey_account(old_coldkey, swap_cost)?; Self::burn_tokens(actual_burn_amount); // 7. Update the weight for the balance operations weight.saturating_accrue(T::DbWeight::get().reads_writes(1, 1)); // 8. Perform the actual coldkey swap - let _ = Self::perform_swap_coldkey(&old_coldkey, new_coldkey, &mut weight); + let _ = Self::perform_swap_coldkey(old_coldkey, new_coldkey, &mut weight); // 9. Update the last transaction block for the new coldkey Self::set_last_tx_block(new_coldkey, Self::get_current_block_as_u64()); weight.saturating_accrue(T::DbWeight::get().writes(1)); - // 10. Emit the ColdkeySwapped event + // 10. Remove the coldkey swap scheduled record + ColdkeySwapScheduled::::remove(old_coldkey); + + // 11. Emit the ColdkeySwapped event Self::deposit_event(Event::ColdkeySwapped { old_coldkey: old_coldkey.clone(), new_coldkey: new_coldkey.clone(), diff --git a/pallets/subtensor/src/swap/swap_hotkey.rs b/pallets/subtensor/src/swap/swap_hotkey.rs index f39948339..793e34bff 100644 --- a/pallets/subtensor/src/swap/swap_hotkey.rs +++ b/pallets/subtensor/src/swap/swap_hotkey.rs @@ -308,6 +308,41 @@ impl Pallet { weight.saturating_accrue(T::DbWeight::get().reads_writes(1, 1)); } + // 11. Swap ChildKeys. + // ChildKeys( parent, netuid ) --> Vec<(proportion,child)> -- the child keys of the parent. + for netuid in Self::get_all_subnet_netuids() { + // Get the children of the old hotkey for this subnet + let my_children: Vec<(u64, T::AccountId)> = ChildKeys::::get(old_hotkey, netuid); + // Remove the old hotkey's child entries + ChildKeys::::remove(old_hotkey, netuid); + // Insert the same child entries for the new hotkey + ChildKeys::::insert(new_hotkey, netuid, my_children); + } + + // 12. Swap ParentKeys. + // ParentKeys( child, netuid ) --> Vec<(proportion,parent)> -- the parent keys of the child. + for netuid in Self::get_all_subnet_netuids() { + // Get the parents of the old hotkey for this subnet + let parents: Vec<(u64, T::AccountId)> = ParentKeys::::get(old_hotkey, netuid); + // Remove the old hotkey's parent entries + ParentKeys::::remove(old_hotkey, netuid); + // Insert the same parent entries for the new hotkey + ParentKeys::::insert(new_hotkey, netuid, parents.clone()); + for (_, parent_key_i) in parents { + // For each parent, update their children list + let mut parent_children: Vec<(u64, T::AccountId)> = + ChildKeys::::get(parent_key_i.clone(), netuid); + for child in parent_children.iter_mut() { + // If the child is the old hotkey, replace it with the new hotkey + if child.1 == *old_hotkey { + child.1 = new_hotkey.clone(); + } + } + // Update the parent's children list + ChildKeys::::insert(parent_key_i, netuid, parent_children); + } + } + // Return successful after swapping all the relevant terms. Ok(()) } diff --git a/pallets/subtensor/src/utils/identity.rs b/pallets/subtensor/src/utils/identity.rs new file mode 100644 index 000000000..460bcb838 --- /dev/null +++ b/pallets/subtensor/src/utils/identity.rs @@ -0,0 +1,109 @@ +use super::*; +use frame_support::ensure; +use frame_system::ensure_signed; +use sp_std::vec::Vec; + +impl Pallet { + /// Sets the identity for a coldkey. + /// + /// This function allows a user to set or update their identity information associated with their coldkey. + /// It checks if the caller has at least one registered hotkey, validates the provided identity information, + /// and then stores it in the blockchain state. + /// + /// # Arguments + /// + /// * `origin` - The origin of the call, which should be a signed extrinsic. + /// * `name` - The name to be associated with the identity. + /// * `url` - A URL associated with the identity. + /// * `image` - An image URL or identifier for the identity. + /// * `discord` - Discord information for the identity. + /// * `description` - A description of the identity. + /// * `additional` - Any additional information for the identity. + /// + /// # Returns + /// + /// Returns `Ok(())` if the identity is successfully set, otherwise returns an error. + pub fn do_set_identity( + origin: T::RuntimeOrigin, + name: Vec, + url: Vec, + image: Vec, + discord: Vec, + description: Vec, + additional: Vec, + ) -> dispatch::DispatchResult { + // Ensure the call is signed and get the signer's (coldkey) account + let coldkey = ensure_signed(origin)?; + + // Retrieve all hotkeys associated with this coldkey + let hotkeys: Vec = OwnedHotkeys::::get(coldkey.clone()); + + // Ensure that at least one of the associated hotkeys is registered on any network + ensure!( + hotkeys + .iter() + .any(|hotkey| Self::is_hotkey_registered_on_any_network(hotkey)), + Error::::HotKeyNotRegisteredInNetwork + ); + + // Create the identity struct with the provided information + let identity = ChainIdentityOf { + name, + url, + image, + discord, + description, + additional, + }; + + // Validate the created identity + ensure!( + Self::is_valid_identity(&identity), + Error::::InvalidIdentity + ); + + // Store the validated identity in the blockchain state + Identities::::insert(coldkey.clone(), identity.clone()); + + // Log the identity set event + log::debug!("ChainIdentitySet( coldkey:{:?} ) ", coldkey.clone()); + + // Emit an event to notify that an identity has been set + Self::deposit_event(Event::ChainIdentitySet(coldkey.clone())); + + // Return Ok to indicate successful execution + Ok(()) + } + + /// Validates the given ChainIdentityOf struct. + /// + /// This function checks if the total length of all fields in the ChainIdentityOf struct + /// is less than or equal to 512 bytes, and if each individual field is also + /// less than or equal to 512 bytes. + /// + /// # Arguments + /// + /// * `identity` - A reference to the ChainIdentityOf struct to be validated. + /// + /// # Returns + /// + /// * `bool` - Returns true if the Identity is valid, false otherwise. + pub fn is_valid_identity(identity: &ChainIdentityOf) -> bool { + let total_length = identity + .name + .len() + .saturating_add(identity.url.len()) + .saturating_add(identity.image.len()) + .saturating_add(identity.discord.len()) + .saturating_add(identity.description.len()) + .saturating_add(identity.additional.len()); + + total_length <= 256 + 256 + 1024 + 256 + 1024 + 1024 + && identity.name.len() <= 256 + && identity.url.len() <= 256 + && identity.image.len() <= 1024 + && identity.discord.len() <= 256 + && identity.description.len() <= 1024 + && identity.additional.len() <= 1024 + } +} diff --git a/pallets/subtensor/src/utils.rs b/pallets/subtensor/src/utils/misc.rs similarity index 85% rename from pallets/subtensor/src/utils.rs rename to pallets/subtensor/src/utils/misc.rs index 2cd49e198..0e1038b18 100644 --- a/pallets/subtensor/src/utils.rs +++ b/pallets/subtensor/src/utils/misc.rs @@ -5,35 +5,9 @@ use crate::{ }; use sp_core::Get; use sp_core::U256; +use sp_runtime::Saturating; use substrate_fixed::types::I32F32; -/// Enum representing different types of transactions -#[derive(Copy, Clone)] -pub enum TransactionType { - SetChildren, - Unknown, -} - -/// Implement conversion from TransactionType to u16 -impl From for u16 { - fn from(tx_type: TransactionType) -> Self { - match tx_type { - TransactionType::SetChildren => 0, - TransactionType::Unknown => 1, - } - } -} - -/// Implement conversion from u16 to TransactionType -impl From for TransactionType { - fn from(value: u16) -> Self { - match value { - 0 => TransactionType::SetChildren, - _ => TransactionType::Unknown, - } - } -} - impl Pallet { pub fn ensure_subnet_owner_or_root( o: T::RuntimeOrigin, @@ -149,12 +123,12 @@ impl Pallet { Active::::insert(netuid, updated_active_vec); } pub fn set_pruning_score_for_uid(netuid: u16, uid: u16, pruning_score: u16) { - log::info!("netuid = {:?}", netuid); - log::info!( + log::debug!("netuid = {:?}", netuid); + log::debug!( "SubnetworkN::::get( netuid ) = {:?}", SubnetworkN::::get(netuid) ); - log::info!("uid = {:?}", uid); + log::debug!("uid = {:?}", uid); assert!(uid < SubnetworkN::::get(netuid)); PruningScores::::mutate(netuid, |v| { if let Some(s) = v.get_mut(uid as usize) { @@ -302,88 +276,6 @@ impl Pallet { Ok(()) } - // ======================== - // ==== Rate Limiting ===== - // ======================== - /// Get the rate limit for a specific transaction type - pub fn get_rate_limit(tx_type: &TransactionType) -> u64 { - match tx_type { - TransactionType::SetChildren => (DefaultTempo::::get().saturating_mul(2)).into(), // Cannot set children twice within the default tempo period. - TransactionType::Unknown => 0, // Default to no limit for unknown types (no limit) - } - } - - /// Check if a transaction should be rate limited on a specific subnet - pub fn passes_rate_limit_on_subnet( - tx_type: &TransactionType, - hotkey: &T::AccountId, - netuid: u16, - ) -> bool { - let block: u64 = Self::get_current_block_as_u64(); - let limit: u64 = Self::get_rate_limit(tx_type); - let last_block: u64 = Self::get_last_transaction_block(hotkey, netuid, tx_type); - block.saturating_sub(last_block) < limit - } - - /// Check if a transaction should be rate limited globally - pub fn passes_rate_limit_globally(tx_type: &TransactionType, hotkey: &T::AccountId) -> bool { - let netuid: u16 = u16::MAX; - let block: u64 = Self::get_current_block_as_u64(); - let limit: u64 = Self::get_rate_limit(tx_type); - let last_block: u64 = Self::get_last_transaction_block(hotkey, netuid, tx_type); - block.saturating_sub(last_block) >= limit - } - - /// Get the block number of the last transaction for a specific hotkey, network, and transaction type - pub fn get_last_transaction_block( - hotkey: &T::AccountId, - netuid: u16, - tx_type: &TransactionType, - ) -> u64 { - let tx_as_u16: u16 = (*tx_type).into(); - TransactionKeyLastBlock::::get((hotkey, netuid, tx_as_u16)) - } - - /// Set the block number of the last transaction for a specific hotkey, network, and transaction type - pub fn set_last_transaction_block( - hotkey: &T::AccountId, - netuid: u16, - tx_type: &TransactionType, - block: u64, - ) { - let tx_as_u16: u16 = (*tx_type).into(); - TransactionKeyLastBlock::::insert((hotkey, netuid, tx_as_u16), block); - } - - pub fn set_last_tx_block(key: &T::AccountId, block: u64) { - LastTxBlock::::insert(key, block) - } - pub fn get_last_tx_block(key: &T::AccountId) -> u64 { - LastTxBlock::::get(key) - } - pub fn set_last_tx_block_delegate_take(key: &T::AccountId, block: u64) { - LastTxBlockDelegateTake::::insert(key, block) - } - pub fn get_last_tx_block_delegate_take(key: &T::AccountId) -> u64 { - LastTxBlockDelegateTake::::get(key) - } - pub fn exceeds_tx_rate_limit(prev_tx_block: u64, current_block: u64) -> bool { - let rate_limit: u64 = Self::get_tx_rate_limit(); - if rate_limit == 0 || prev_tx_block == 0 { - return false; - } - - current_block.saturating_sub(prev_tx_block) <= rate_limit - } - pub fn exceeds_tx_delegate_take_rate_limit(prev_tx_block: u64, current_block: u64) -> bool { - let rate_limit: u64 = Self::get_tx_delegate_take_rate_limit(); - if rate_limit == 0 || prev_tx_block == 0 { - return false; - } - - current_block.saturating_sub(prev_tx_block) <= rate_limit - } - // ======================== // === Token Management === // ======================== @@ -393,25 +285,20 @@ impl Pallet { pub fn coinbase(amount: u64) { TotalIssuance::::put(TotalIssuance::::get().saturating_add(amount)); } - pub fn get_default_take() -> u16 { - // Default to maximum - MaxTake::::get() - } - pub fn set_max_take(default_take: u16) { - MaxTake::::put(default_take); - Self::deposit_event(Event::DefaultTakeSet(default_take)); - } - pub fn get_min_take() -> u16 { - MinTake::::get() - } pub fn set_subnet_locked_balance(netuid: u16, amount: u64) { SubnetLocked::::insert(netuid, amount); } - pub fn get_subnet_locked_balance(netuid: u16) -> u64 { SubnetLocked::::get(netuid) } + pub fn get_total_subnet_locked() -> u64 { + let mut total_subnet_locked: u64 = 0; + for (_, locked) in SubnetLocked::::iter() { + total_subnet_locked.saturating_accrue(locked); + } + total_subnet_locked + } // ======================== // ========= Sudo ========= @@ -433,18 +320,49 @@ impl Pallet { Self::deposit_event(Event::TxDelegateTakeRateLimitSet(tx_rate_limit)); } pub fn set_min_delegate_take(take: u16) { - MinTake::::put(take); + MinDelegateTake::::put(take); Self::deposit_event(Event::MinDelegateTakeSet(take)); } pub fn set_max_delegate_take(take: u16) { - MaxTake::::put(take); + MaxDelegateTake::::put(take); Self::deposit_event(Event::MaxDelegateTakeSet(take)); } pub fn get_min_delegate_take() -> u16 { - MinTake::::get() + MinDelegateTake::::get() } pub fn get_max_delegate_take() -> u16 { - MaxTake::::get() + MaxDelegateTake::::get() + } + pub fn get_default_delegate_take() -> u16 { + // Default to maximum + MaxDelegateTake::::get() + } + // get_default_childkey_take + pub fn get_default_childkey_take() -> u16 { + // Default to maximum + MinChildkeyTake::::get() + } + pub fn get_tx_childkey_take_rate_limit() -> u64 { + TxChildkeyTakeRateLimit::::get() + } + pub fn set_tx_childkey_take_rate_limit(tx_rate_limit: u64) { + TxChildkeyTakeRateLimit::::put(tx_rate_limit); + Self::deposit_event(Event::TxChildKeyTakeRateLimitSet(tx_rate_limit)); + } + pub fn set_min_childkey_take(take: u16) { + MinChildkeyTake::::put(take); + Self::deposit_event(Event::MinChildKeyTakeSet(take)); + } + pub fn set_max_childkey_take(take: u16) { + MaxChildkeyTake::::put(take); + Self::deposit_event(Event::MaxChildKeyTakeSet(take)); + } + pub fn get_min_childkey_take() -> u16 { + MinChildkeyTake::::get() + } + + pub fn get_max_childkey_take() -> u16 { + MaxChildkeyTake::::get() } pub fn get_serving_rate_limit(netuid: u16) -> u64 { @@ -537,6 +455,13 @@ impl Pallet { ImmunityPeriod::::insert(netuid, immunity_period); Self::deposit_event(Event::ImmunityPeriodSet(netuid, immunity_period)); } + /// Check if a neuron is in immunity based on the current block + pub fn get_neuron_is_immune(netuid: u16, uid: u16) -> bool { + let registered_at = Self::get_neuron_block_at_registration(netuid, uid); + let current_block = Self::get_current_block_as_u64(); + let immunity_period = Self::get_immunity_period(netuid); + current_block.saturating_sub(registered_at) < u64::from(immunity_period) + } pub fn get_min_allowed_weights(netuid: u16) -> u16 { MinAllowedWeights::::get(netuid) diff --git a/pallets/subtensor/src/utils/mod.rs b/pallets/subtensor/src/utils/mod.rs new file mode 100644 index 000000000..a42c91119 --- /dev/null +++ b/pallets/subtensor/src/utils/mod.rs @@ -0,0 +1,5 @@ +use super::*; +pub mod identity; +pub mod misc; +pub mod rate_limiting; +pub mod try_state; diff --git a/pallets/subtensor/src/utils/rate_limiting.rs b/pallets/subtensor/src/utils/rate_limiting.rs new file mode 100644 index 000000000..b02ad9855 --- /dev/null +++ b/pallets/subtensor/src/utils/rate_limiting.rs @@ -0,0 +1,125 @@ +use super::*; +use sp_core::Get; + +/// Enum representing different types of transactions +#[derive(Copy, Clone)] +pub enum TransactionType { + SetChildren, + SetChildkeyTake, + Unknown, +} + +/// Implement conversion from TransactionType to u16 +impl From for u16 { + fn from(tx_type: TransactionType) -> Self { + match tx_type { + TransactionType::SetChildren => 0, + TransactionType::SetChildkeyTake => 1, + TransactionType::Unknown => 2, + } + } +} + +/// Implement conversion from u16 to TransactionType +impl From for TransactionType { + fn from(value: u16) -> Self { + match value { + 0 => TransactionType::SetChildren, + 1 => TransactionType::SetChildkeyTake, + _ => TransactionType::Unknown, + } + } +} +impl Pallet { + // ======================== + // ==== Rate Limiting ===== + // ======================== + /// Get the rate limit for a specific transaction type + pub fn get_rate_limit(tx_type: &TransactionType) -> u64 { + match tx_type { + TransactionType::SetChildren => (DefaultTempo::::get().saturating_mul(2)).into(), // Cannot set children twice within the default tempo period. + TransactionType::SetChildkeyTake => TxChildkeyTakeRateLimit::::get(), + TransactionType::Unknown => 0, // Default to no limit for unknown types (no limit) + } + } + + /// Check if a transaction should be rate limited on a specific subnet + pub fn passes_rate_limit_on_subnet( + tx_type: &TransactionType, + hotkey: &T::AccountId, + netuid: u16, + ) -> bool { + let block: u64 = Self::get_current_block_as_u64(); + let limit: u64 = Self::get_rate_limit(tx_type); + let last_block: u64 = Self::get_last_transaction_block(hotkey, netuid, tx_type); + + // Allow the first transaction (when last_block is 0) or if the rate limit has passed + last_block == 0 || block.saturating_sub(last_block) >= limit + } + + /// Check if a transaction should be rate limited globally + pub fn passes_rate_limit_globally(tx_type: &TransactionType, hotkey: &T::AccountId) -> bool { + let netuid: u16 = u16::MAX; + let block: u64 = Self::get_current_block_as_u64(); + let limit: u64 = Self::get_rate_limit(tx_type); + let last_block: u64 = Self::get_last_transaction_block(hotkey, netuid, tx_type); + block.saturating_sub(last_block) >= limit + } + + /// Get the block number of the last transaction for a specific hotkey, network, and transaction type + pub fn get_last_transaction_block( + hotkey: &T::AccountId, + netuid: u16, + tx_type: &TransactionType, + ) -> u64 { + let tx_as_u16: u16 = (*tx_type).into(); + TransactionKeyLastBlock::::get((hotkey, netuid, tx_as_u16)) + } + + /// Set the block number of the last transaction for a specific hotkey, network, and transaction type + pub fn set_last_transaction_block( + hotkey: &T::AccountId, + netuid: u16, + tx_type: &TransactionType, + block: u64, + ) { + let tx_as_u16: u16 = (*tx_type).into(); + TransactionKeyLastBlock::::insert((hotkey, netuid, tx_as_u16), block); + } + + pub fn set_last_tx_block(key: &T::AccountId, block: u64) { + LastTxBlock::::insert(key, block) + } + pub fn get_last_tx_block(key: &T::AccountId) -> u64 { + LastTxBlock::::get(key) + } + pub fn set_last_tx_block_delegate_take(key: &T::AccountId, block: u64) { + LastTxBlockDelegateTake::::insert(key, block) + } + pub fn get_last_tx_block_delegate_take(key: &T::AccountId) -> u64 { + LastTxBlockDelegateTake::::get(key) + } + + pub fn set_last_tx_block_childkey_take(key: &T::AccountId, block: u64) { + LastTxBlockChildKeyTake::::insert(key, block) + } + pub fn get_last_tx_block_childkey_take(key: &T::AccountId) -> u64 { + LastTxBlockChildKeyTake::::get(key) + } + pub fn exceeds_tx_rate_limit(prev_tx_block: u64, current_block: u64) -> bool { + let rate_limit: u64 = Self::get_tx_rate_limit(); + if rate_limit == 0 || prev_tx_block == 0 { + return false; + } + + current_block.saturating_sub(prev_tx_block) <= rate_limit + } + pub fn exceeds_tx_delegate_take_rate_limit(prev_tx_block: u64, current_block: u64) -> bool { + let rate_limit: u64 = Self::get_tx_delegate_take_rate_limit(); + if rate_limit == 0 || prev_tx_block == 0 { + return false; + } + + current_block.saturating_sub(prev_tx_block) <= rate_limit + } +} diff --git a/pallets/subtensor/src/utils/try_state.rs b/pallets/subtensor/src/utils/try_state.rs new file mode 100644 index 000000000..4763c0484 --- /dev/null +++ b/pallets/subtensor/src/utils/try_state.rs @@ -0,0 +1,49 @@ +use super::*; + +impl Pallet { + /// Checks if the accounting invariants for [`TotalStake`], [`TotalSubnetLocked`], and [`TotalIssuance`] are correct. + /// + /// This function verifies that: + /// 1. The sum of all stakes matches the [`TotalStake`]. + /// 2. The [`TotalSubnetLocked`] is correctly calculated. + /// 3. The [`TotalIssuance`] equals the sum of currency issuance, total stake, and total subnet locked. + /// + /// # Returns + /// + /// Returns `Ok(())` if all invariants are correct, otherwise returns an error. + #[cfg(feature = "try-runtime")] + pub fn check_accounting_invariants() -> Result<(), sp_runtime::TryRuntimeError> { + use frame_support::traits::fungible::Inspect; + + // Calculate the total staked amount + let mut total_staked: u64 = 0; + for (_account, _netuid, stake) in Stake::::iter() { + total_staked = total_staked.saturating_add(stake); + } + + // Verify that the calculated total stake matches the stored TotalStake + ensure!( + total_staked == TotalStake::::get(), + "TotalStake does not match total staked", + ); + + // Get the total subnet locked amount + let total_subnet_locked: u64 = Self::get_total_subnet_locked(); + + // Get the total currency issuance + let currency_issuance: u64 = T::Currency::total_issuance(); + + // Calculate the expected total issuance + let expected_total_issuance: u64 = currency_issuance + .saturating_add(total_staked) + .saturating_add(total_subnet_locked); + + // Verify that the calculated total issuance matches the stored TotalIssuance + ensure!( + TotalIssuance::::get() == expected_total_issuance, + "TotalIssuance accounting discrepancy", + ); + + Ok(()) + } +} diff --git a/pallets/subtensor/tests/children.rs b/pallets/subtensor/tests/children.rs index e834baa85..2b99030ab 100644 --- a/pallets/subtensor/tests/children.rs +++ b/pallets/subtensor/tests/children.rs @@ -1,9 +1,11 @@ +#![allow(clippy::indexing_slicing)] use crate::mock::*; -use frame_support::{assert_err, assert_ok}; +use frame_support::{assert_err, assert_noop, assert_ok}; mod mock; -use pallet_subtensor::*; +use pallet_subtensor::{utils::rate_limiting::TransactionType, *}; use sp_core::U256; +// 1: Successful setting of a single child // SKIP_WASM_BUILD=1 RUST_LOG=info cargo test --test children -- test_do_set_child_singular_success --exact --nocapture #[test] fn test_do_set_child_singular_success() { @@ -32,6 +34,7 @@ fn test_do_set_child_singular_success() { }); } +// 2: Attempt to set child in non-existent network // SKIP_WASM_BUILD=1 RUST_LOG=info cargo test --test children -- test_do_set_child_singular_network_does_not_exist --exact --nocapture #[test] fn test_do_set_child_singular_network_does_not_exist() { @@ -55,6 +58,7 @@ fn test_do_set_child_singular_network_does_not_exist() { }); } +// 3: Attempt to set invalid child (same as hotkey) // SKIP_WASM_BUILD=1 RUST_LOG=info cargo test --test children -- test_do_set_child_singular_invalid_child --exact --nocapture #[test] fn test_do_set_child_singular_invalid_child() { @@ -83,6 +87,7 @@ fn test_do_set_child_singular_invalid_child() { }); } +// 4: Attempt to set child with non-associated coldkey // SKIP_WASM_BUILD=1 RUST_LOG=info cargo test --test children -- test_do_set_child_singular_non_associated_coldkey --exact --nocapture #[test] fn test_do_set_child_singular_non_associated_coldkey() { @@ -110,6 +115,7 @@ fn test_do_set_child_singular_non_associated_coldkey() { }); } +// 5: Attempt to set child in root network // SKIP_WASM_BUILD=1 RUST_LOG=info cargo test --test children -- test_do_set_child_singular_root_network --exact --nocapture #[test] fn test_do_set_child_singular_root_network() { @@ -136,6 +142,13 @@ fn test_do_set_child_singular_root_network() { }); } +// 6: Cleanup of old children when setting new ones +// This test verifies that when new children are set, the old ones are properly removed. +// It checks: +// - Setting an initial child +// - Replacing it with a new child +// - Ensuring the old child is no longer associated +// - Confirming the new child is correctly assigned // SKIP_WASM_BUILD=1 RUST_LOG=info cargo test --test children -- test_do_set_child_singular_old_children_cleanup --exact --nocapture #[test] fn test_do_set_child_singular_old_children_cleanup() { @@ -177,7 +190,13 @@ fn test_do_set_child_singular_old_children_cleanup() { }); } -// SKIP_WASM_BUILD=1 RUST_LOG=info cargo test --test children -- test_do_set_child_singular_old_children_cleanup --exact --nocapture +// 7: Verify new children assignment +// This test checks if new children are correctly assigned to a parent. +// It verifies: +// - Setting a child for a parent +// - Confirming the child is correctly listed under the parent +// - Ensuring the parent is correctly listed for the child +// SKIP_WASM_BUILD=1 RUST_LOG=info cargo test --test children -- test_do_set_child_singular_new_children_assignment --exact --nocapture #[test] fn test_do_set_child_singular_new_children_assignment() { new_test_ext(1).execute_with(|| { @@ -209,6 +228,12 @@ fn test_do_set_child_singular_new_children_assignment() { }); } +// 8: Test edge cases for proportion values +// This test verifies that the system correctly handles minimum and maximum proportion values. +// It checks: +// - Setting a child with the minimum possible proportion (0) +// - Setting a child with the maximum possible proportion (u64::MAX) +// - Confirming both assignments are processed correctly // SKIP_WASM_BUILD=1 RUST_LOG=info cargo test --test children -- test_do_set_child_singular_proportion_edge_cases --exact --nocapture #[test] fn test_do_set_child_singular_proportion_edge_cases() { @@ -250,6 +275,13 @@ fn test_do_set_child_singular_proportion_edge_cases() { }); } +// 9: Test setting multiple children +// This test verifies that when multiple children are set, only the last one remains. +// It checks: +// - Setting an initial child +// - Setting a second child +// - Confirming only the second child remains associated +// - Verifying the first child is no longer associated // SKIP_WASM_BUILD=1 RUST_LOG=info cargo test --test children -- test_do_set_child_singular_multiple_children --exact --nocapture #[test] fn test_do_set_child_singular_multiple_children() { @@ -295,6 +327,12 @@ fn test_do_set_child_singular_multiple_children() { }); } +// 10: Test adding a singular child with various error conditions +// This test checks different scenarios when adding a child, including: +// - Attempting to set a child in a non-existent network +// - Trying to set a child with an unassociated coldkey +// - Setting an invalid child +// - Successfully setting a valid child // SKIP_WASM_BUILD=1 RUST_LOG=info cargo test --test children -- test_add_singular_child --exact --nocapture #[test] fn test_add_singular_child() { @@ -342,73 +380,67 @@ fn test_add_singular_child() { }) } +// 11: Test getting stake for a hotkey on a subnet +// This test verifies the correct calculation of stake for a parent and child neuron: +// - Sets up a network with a parent and child neuron +// - Stakes tokens to both parent and child from different coldkeys +// - Establishes a parent-child relationship with 100% stake allocation +// - Checks that the parent's stake is correctly transferred to the child +// - Ensures the total stake is preserved in the system // SKIP_WASM_BUILD=1 RUST_LOG=info cargo test --test children -- test_get_stake_for_hotkey_on_subnet --exact --nocapture #[test] fn test_get_stake_for_hotkey_on_subnet() { new_test_ext(1).execute_with(|| { let netuid: u16 = 1; - let hotkey0 = U256::from(1); - let hotkey1 = U256::from(2); - let coldkey0 = U256::from(3); - let coldkey1 = U256::from(4); + let parent = U256::from(1); + let child = U256::from(2); + let coldkey1 = U256::from(3); + let coldkey2 = U256::from(4); add_network(netuid, 0, 0); - - let max_stake: u64 = 3000; - SubtensorModule::set_network_max_stake(netuid, max_stake); - - SubtensorModule::create_account_if_non_existent(&coldkey0, &hotkey0); - SubtensorModule::create_account_if_non_existent(&coldkey1, &hotkey1); - - SubtensorModule::increase_stake_on_coldkey_hotkey_account(&coldkey0, &hotkey0, 1000); - SubtensorModule::increase_stake_on_coldkey_hotkey_account(&coldkey0, &hotkey1, 1000); - SubtensorModule::increase_stake_on_coldkey_hotkey_account(&coldkey1, &hotkey0, 1000); - SubtensorModule::increase_stake_on_coldkey_hotkey_account(&coldkey1, &hotkey1, 1000); - - assert_eq!(SubtensorModule::get_total_stake_for_hotkey(&hotkey0), 2000); - assert_eq!(SubtensorModule::get_total_stake_for_hotkey(&hotkey1), 2000); - - assert_eq!( - SubtensorModule::get_stake_for_hotkey_on_subnet(&hotkey0, netuid), - 2000 - ); - assert_eq!( - SubtensorModule::get_stake_for_hotkey_on_subnet(&hotkey1, netuid), - 2000 - ); - - // Set child relationship + register_ok_neuron(netuid, parent, coldkey1, 0); + register_ok_neuron(netuid, child, coldkey2, 0); + + // Stake 1000 to parent from coldkey1 + SubtensorModule::increase_stake_on_coldkey_hotkey_account(&coldkey1, &parent, 1000); + // Stake 1000 to parent from coldkey2 + SubtensorModule::increase_stake_on_coldkey_hotkey_account(&coldkey2, &parent, 1000); + // Stake 1000 to child from coldkey1 + SubtensorModule::increase_stake_on_coldkey_hotkey_account(&coldkey1, &child, 1000); + // Stake 1000 to child from coldkey2 + SubtensorModule::increase_stake_on_coldkey_hotkey_account(&coldkey2, &child, 1000); + + // Set parent-child relationship with 100% stake allocation assert_ok!(SubtensorModule::do_set_children( - RuntimeOrigin::signed(coldkey0), - hotkey0, + RuntimeOrigin::signed(coldkey1), + parent, netuid, - vec![(u64::MAX, hotkey1)] + vec![(u64::MAX, child)] )); - // Check stakes after setting child - let stake0 = SubtensorModule::get_stake_for_hotkey_on_subnet(&hotkey0, netuid); - let stake1 = SubtensorModule::get_stake_for_hotkey_on_subnet(&hotkey1, netuid); + let parent_stake = SubtensorModule::get_stake_for_hotkey_on_subnet(&parent, netuid); + let child_stake = SubtensorModule::get_stake_for_hotkey_on_subnet(&child, netuid); - assert_eq!(stake0, 0); - assert_eq!(stake1, max_stake); - - // Change child relationship to 50% - assert_ok!(SubtensorModule::do_set_children( - RuntimeOrigin::signed(coldkey0), - hotkey0, - netuid, - vec![(u64::MAX / 2, hotkey1)] - )); + log::info!("Parent stake: {}", parent_stake); + log::info!("Child stake: {}", child_stake); - // Check stakes after changing child relationship - let stake0 = SubtensorModule::get_stake_for_hotkey_on_subnet(&hotkey0, netuid); - let stake1 = SubtensorModule::get_stake_for_hotkey_on_subnet(&hotkey1, netuid); + // The parent should have 0 stake as it's all allocated to the child + assert_eq!(parent_stake, 0); + // The child should have its original stake (2000) plus the parent's stake (2000) + assert_eq!(child_stake, 4000); - assert_eq!(stake0, 1001); - assert!(stake1 >= max_stake - 1 && stake1 <= max_stake); + // Ensure total stake is preserved + assert_eq!(parent_stake + child_stake, 4000); }); } +// 12: Test revoking a singular child successfully +// This test checks the process of revoking a child neuron: +// - Sets up a network with a parent and child neuron +// - Establishes a parent-child relationship +// - Revokes the child relationship +// - Verifies that the child is removed from the parent's children list +// - Ensures the parent is removed from the child's parents list // SKIP_WASM_BUILD=1 RUST_LOG=info cargo test --test children -- test_do_revoke_child_singular_success --exact --nocapture #[test] fn test_do_revoke_child_singular_success() { @@ -453,6 +485,10 @@ fn test_do_revoke_child_singular_success() { }); } +// 13: Test revoking a child in a non-existent network +// This test verifies that attempting to revoke a child in a non-existent network results in an error: +// - Attempts to revoke a child in a network that doesn't exist +// - Checks that the appropriate error is returned // SKIP_WASM_BUILD=1 RUST_LOG=info cargo test --test children -- test_do_revoke_child_singular_network_does_not_exist --exact --nocapture #[test] fn test_do_revoke_child_singular_network_does_not_exist() { @@ -474,6 +510,11 @@ fn test_do_revoke_child_singular_network_does_not_exist() { }); } +// 14: Test revoking a child with a non-associated coldkey +// This test ensures that attempting to revoke a child using an unassociated coldkey results in an error: +// - Sets up a network with a hotkey registered to a different coldkey +// - Attempts to revoke a child using an unassociated coldkey +// - Verifies that the appropriate error is returned // SKIP_WASM_BUILD=1 RUST_LOG=info cargo test --test children -- test_do_revoke_child_singular_non_associated_coldkey --exact --nocapture #[test] fn test_do_revoke_child_singular_non_associated_coldkey() { @@ -499,6 +540,11 @@ fn test_do_revoke_child_singular_non_associated_coldkey() { }); } +// 15: Test revoking a non-associated child +// This test verifies that attempting to revoke a child that is not associated with the parent results in an error: +// - Sets up a network and registers a hotkey +// - Attempts to revoke a child that was never associated with the parent +// - Checks that the appropriate error is returned // SKIP_WASM_BUILD=1 RUST_LOG=info cargo test --test children -- test_do_revoke_child_singular_child_not_associated --exact --nocapture #[test] fn test_do_revoke_child_singular_child_not_associated() { @@ -523,6 +569,12 @@ fn test_do_revoke_child_singular_child_not_associated() { }); } +// 16: Test setting multiple children successfully +// This test verifies that multiple children can be set for a parent successfully: +// - Sets up a network and registers a hotkey +// - Sets multiple children with different proportions +// - Verifies that the children are correctly assigned to the parent +// - Checks that the parent is correctly assigned to each child // SKIP_WASM_BUILD=1 RUST_LOG=info cargo test --test children -- test_do_set_children_multiple_success --exact --nocapture #[test] fn test_do_set_children_multiple_success() { @@ -560,6 +612,10 @@ fn test_do_set_children_multiple_success() { }); } +// 17: Test setting multiple children in a non-existent network +// This test ensures that attempting to set multiple children in a non-existent network results in an error: +// - Attempts to set children in a network that doesn't exist +// - Verifies that the appropriate error is returned // SKIP_WASM_BUILD=1 RUST_LOG=info cargo test --test children -- test_do_set_children_multiple_network_does_not_exist --exact --nocapture #[test] fn test_do_set_children_multiple_network_does_not_exist() { @@ -583,6 +639,11 @@ fn test_do_set_children_multiple_network_does_not_exist() { }); } +// 18: Test setting multiple children with an invalid child +// This test verifies that attempting to set multiple children with an invalid child (same as parent) results in an error: +// - Sets up a network and registers a hotkey +// - Attempts to set a child that is the same as the parent hotkey +// - Checks that the appropriate error is returned // SKIP_WASM_BUILD=1 RUST_LOG=info cargo test --test children -- test_do_set_children_multiple_invalid_child --exact --nocapture #[test] fn test_do_set_children_multiple_invalid_child() { @@ -609,6 +670,11 @@ fn test_do_set_children_multiple_invalid_child() { }); } +// 19: Test setting multiple children with a non-associated coldkey +// This test ensures that attempting to set multiple children using an unassociated coldkey results in an error: +// - Sets up a network with a hotkey registered to a different coldkey +// - Attempts to set children using an unassociated coldkey +// - Verifies that the appropriate error is returned // SKIP_WASM_BUILD=1 RUST_LOG=info cargo test --test children -- test_do_set_children_multiple_non_associated_coldkey --exact --nocapture #[test] fn test_do_set_children_multiple_non_associated_coldkey() { @@ -636,6 +702,11 @@ fn test_do_set_children_multiple_non_associated_coldkey() { }); } +// 20: Test setting multiple children in root network +// This test verifies that attempting to set children in the root network results in an error: +// - Sets up the root network +// - Attempts to set children in the root network +// - Checks that the appropriate error is returned // SKIP_WASM_BUILD=1 RUST_LOG=info cargo test --test children -- test_do_set_children_multiple_root_network --exact --nocapture #[test] fn test_do_set_children_multiple_root_network() { @@ -662,6 +733,13 @@ fn test_do_set_children_multiple_root_network() { }); } +// 21: Test cleanup of old children when setting multiple new ones +// This test ensures that when new children are set, the old ones are properly removed: +// - Sets up a network and registers a hotkey +// - Sets an initial child +// - Replaces it with multiple new children +// - Verifies that the old child is no longer associated +// - Confirms the new children are correctly assigned // SKIP_WASM_BUILD=1 RUST_LOG=info cargo test --test children -- test_do_set_children_multiple_old_children_cleanup --exact --nocapture #[test] fn test_do_set_children_multiple_old_children_cleanup() { @@ -707,6 +785,11 @@ fn test_do_set_children_multiple_old_children_cleanup() { }); } +// 22: Test setting multiple children with edge case proportions +// This test verifies the behavior when setting multiple children with minimum and maximum proportions: +// - Sets up a network and registers a hotkey +// - Sets two children with minimum and maximum proportions respectively +// - Verifies that the children are correctly assigned with their respective proportions // SKIP_WASM_BUILD=1 RUST_LOG=info cargo test --test children -- test_do_set_children_multiple_proportion_edge_cases --exact --nocapture #[test] fn test_do_set_children_multiple_proportion_edge_cases() { @@ -740,6 +823,13 @@ fn test_do_set_children_multiple_proportion_edge_cases() { }); } +// 23: Test overwriting existing children with new ones +// This test ensures that when new children are set, they correctly overwrite the existing ones: +// - Sets up a network and registers a hotkey +// - Sets initial children +// - Overwrites with new children +// - Verifies that the final children assignment is correct +// - Checks that old children are properly removed and new ones are correctly assigned // SKIP_WASM_BUILD=1 RUST_LOG=info cargo test --test children -- test_do_set_children_multiple_overwrite_existing --exact --nocapture #[test] fn test_do_set_children_multiple_overwrite_existing() { @@ -791,6 +881,267 @@ fn test_do_set_children_multiple_overwrite_existing() { }); } +// 24: Test childkey take functionality +// This test verifies the functionality of setting and getting childkey take: +// - Sets up a network and registers a hotkey +// - Checks default and maximum childkey take values +// - Sets a new childkey take value +// - Verifies the new take value is stored correctly +// - Attempts to set an invalid take value and checks for appropriate error +// - Tries to set take with a non-associated coldkey and verifies the error +// SKIP_WASM_BUILD=1 RUST_LOG=info cargo test --test children -- test_childkey_take_functionality --exact --nocapture +#[test] +fn test_childkey_take_functionality() { + new_test_ext(1).execute_with(|| { + let coldkey = U256::from(1); + let hotkey = U256::from(2); + let netuid: u16 = 1; + + // Add network and register hotkey + add_network(netuid, 13, 0); + register_ok_neuron(netuid, hotkey, coldkey, 0); + + // Test default and max childkey take + let default_take = SubtensorModule::get_default_childkey_take(); + let min_take = SubtensorModule::get_min_childkey_take(); + log::info!("Default take: {}, Max take: {}", default_take, min_take); + + // Check if default take and max take are the same + assert_eq!( + default_take, min_take, + "Default take should be equal to max take" + ); + + // Log the actual value of MaxChildkeyTake + log::info!( + "MaxChildkeyTake value: {:?}", + MaxChildkeyTake::::get() + ); + + // Test setting childkey take + let new_take: u16 = SubtensorModule::get_max_childkey_take() / 2; // 50% of max_take + assert_ok!(SubtensorModule::set_childkey_take( + RuntimeOrigin::signed(coldkey), + hotkey, + netuid, + new_take + )); + + // Verify childkey take was set correctly + let stored_take = SubtensorModule::get_childkey_take(&hotkey, netuid); + log::info!("Stored take: {}", stored_take); + assert_eq!(stored_take, new_take); + + // Test setting childkey take outside of allowed range + let invalid_take: u16 = SubtensorModule::get_max_childkey_take() + 1; + assert_noop!( + SubtensorModule::set_childkey_take( + RuntimeOrigin::signed(coldkey), + hotkey, + netuid, + invalid_take + ), + Error::::InvalidChildkeyTake + ); + + // Test setting childkey take with non-associated coldkey + let non_associated_coldkey = U256::from(999); + assert_noop!( + SubtensorModule::set_childkey_take( + RuntimeOrigin::signed(non_associated_coldkey), + hotkey, + netuid, + new_take + ), + Error::::NonAssociatedColdKey + ); + }); +} + +// 25: Test childkey take rate limiting +// This test verifies the rate limiting functionality for setting childkey take: +// - Sets up a network and registers a hotkey +// - Sets a rate limit for childkey take changes +// - Performs multiple attempts to set childkey take +// - Verifies that rate limiting prevents frequent changes +// - Advances blocks to bypass rate limit and confirms successful change +// SKIP_WASM_BUILD=1 RUST_LOG=info cargo test --test children -- test_childkey_take_rate_limiting --exact --nocapture +#[test] +fn test_childkey_take_rate_limiting() { + new_test_ext(1).execute_with(|| { + let coldkey = U256::from(1); + let hotkey = U256::from(2); + let netuid: u16 = 1; + + // Add network and register hotkey + add_network(netuid, 13, 0); + register_ok_neuron(netuid, hotkey, coldkey, 0); + + // Set a rate limit for childkey take changes + let rate_limit: u64 = 100; + SubtensorModule::set_tx_childkey_take_rate_limit(rate_limit); + + log::info!( + "Set TxChildkeyTakeRateLimit: {:?}", + TxChildkeyTakeRateLimit::::get() + ); + + // Helper function to log rate limit information + let log_rate_limit_info = || { + let current_block = SubtensorModule::get_current_block_as_u64(); + let last_block = SubtensorModule::get_last_transaction_block( + &hotkey, + netuid, + &TransactionType::SetChildkeyTake, + ); + let passes = SubtensorModule::passes_rate_limit_on_subnet( + &TransactionType::SetChildkeyTake, + &hotkey, + netuid, + ); + let limit = SubtensorModule::get_rate_limit(&TransactionType::SetChildkeyTake); + log::info!( + "Rate limit info: current_block: {}, last_block: {}, limit: {}, passes: {}, diff: {}", + current_block, + last_block, + limit, + passes, + current_block.saturating_sub(last_block) + ); + }; + + // First transaction (should succeed) + log_rate_limit_info(); + assert_ok!(SubtensorModule::set_childkey_take( + RuntimeOrigin::signed(coldkey), + hotkey, + netuid, + 500 + )); + log_rate_limit_info(); + + // Second transaction (should fail due to rate limit) + log_rate_limit_info(); + assert_noop!( + SubtensorModule::set_childkey_take(RuntimeOrigin::signed(coldkey), hotkey, netuid, 600), + Error::::TxChildkeyTakeRateLimitExceeded + ); + log_rate_limit_info(); + + // Advance the block number to just before the rate limit + run_to_block(rate_limit - 1); + + // Third transaction (should still fail) + log_rate_limit_info(); + assert_noop!( + SubtensorModule::set_childkey_take(RuntimeOrigin::signed(coldkey), hotkey, netuid, 650), + Error::::TxChildkeyTakeRateLimitExceeded + ); + log_rate_limit_info(); + + // Advance the block number to just after the rate limit + run_to_block(rate_limit + 1); + + // Fourth transaction (should succeed) + log_rate_limit_info(); + assert_ok!(SubtensorModule::set_childkey_take( + RuntimeOrigin::signed(coldkey), + hotkey, + netuid, + 700 + )); + log_rate_limit_info(); + + // Verify the final take was set + let stored_take = SubtensorModule::get_childkey_take(&hotkey, netuid); + assert_eq!(stored_take, 700); + }); +} + +// 26: Test childkey take functionality across multiple networks +// This test verifies the childkey take functionality across multiple networks: +// - Creates multiple networks and sets up neurons +// - Sets unique childkey take values for each network +// - Verifies that each network has a different childkey take value +// - Attempts to set childkey take again (should fail due to rate limit) +// - Advances blocks to bypass rate limit and successfully updates take value +// SKIP_WASM_BUILD=1 RUST_LOG=info cargo test --test children -- test_multiple_networks_childkey_take --exact --nocapture +#[test] +fn test_multiple_networks_childkey_take() { + new_test_ext(1).execute_with(|| { + const NUM_NETWORKS: u16 = 10; + let coldkey = U256::from(1); + let hotkey = U256::from(2); + + // Create 10 networks and set up neurons (skip network 0) + for netuid in 1..NUM_NETWORKS { + // Add network + add_network(netuid, 13, 0); + + // Register neuron + register_ok_neuron(netuid, hotkey, coldkey, 0); + + // Set a unique childkey take value for each network + let take_value = (netuid + 1) * 100; // Values will be 200, 300, ..., 1000 + assert_ok!(SubtensorModule::set_childkey_take( + RuntimeOrigin::signed(coldkey), + hotkey, + netuid, + take_value + )); + + // Verify the childkey take was set correctly + let stored_take = SubtensorModule::get_childkey_take(&hotkey, netuid); + assert_eq!( + stored_take, take_value, + "Childkey take not set correctly for network {}", + netuid + ); + + // Log the set value + log::info!("Network {}: Childkey take set to {}", netuid, take_value); + } + + // Verify all networks have different childkey take values + for i in 1..NUM_NETWORKS { + for j in (i + 1)..NUM_NETWORKS { + let take_i = SubtensorModule::get_childkey_take(&hotkey, i); + let take_j = SubtensorModule::get_childkey_take(&hotkey, j); + assert_ne!( + take_i, take_j, + "Childkey take values should be different for networks {} and {}", + i, j + ); + } + } + + // Attempt to set childkey take again (should fail due to rate limit) + let result = + SubtensorModule::set_childkey_take(RuntimeOrigin::signed(coldkey), hotkey, 1, 1100); + assert_noop!(result, Error::::TxChildkeyTakeRateLimitExceeded); + + // Advance blocks to bypass rate limit + run_to_block(SubtensorModule::get_tx_childkey_take_rate_limit() + 1); + + // Now setting childkey take should succeed + assert_ok!(SubtensorModule::set_childkey_take( + RuntimeOrigin::signed(coldkey), + hotkey, + 1, + 1100 + )); + + // Verify the new take value + let new_take = SubtensorModule::get_childkey_take(&hotkey, 1); + assert_eq!(new_take, 1100, "Childkey take not updated after rate limit"); + }); +} + +// 27: Test setting children with an empty list +// This test verifies the behavior of setting an empty children list: +// - Adds a network and registers a hotkey +// - Sets an empty children list for the hotkey +// - Verifies that the children assignment is empty // SKIP_WASM_BUILD=1 RUST_LOG=info cargo test --test children -- test_do_set_children_multiple_empty_list --exact --nocapture #[test] fn test_do_set_children_multiple_empty_list() { @@ -817,6 +1168,13 @@ fn test_do_set_children_multiple_empty_list() { }); } +// 28: Test revoking multiple children successfully +// This test verifies the successful revocation of multiple children: +// - Adds a network and registers a hotkey +// - Sets multiple children for the hotkey +// - Revokes all children by setting an empty list +// - Verifies that the children list is empty +// - Verifies that the parent-child relationships are removed for both children // SKIP_WASM_BUILD=1 RUST_LOG=info cargo test --test children -- test_do_revoke_children_multiple_success --exact --nocapture #[test] fn test_do_revoke_children_multiple_success() { @@ -862,6 +1220,10 @@ fn test_do_revoke_children_multiple_success() { }); } +// 29: Test revoking children when network does not exist +// This test verifies the behavior when attempting to revoke children on a non-existent network: +// - Attempts to revoke children on a network that doesn't exist +// - Verifies that the operation fails with the correct error // SKIP_WASM_BUILD=1 RUST_LOG=info cargo test --test children -- test_do_revoke_children_multiple_network_does_not_exist --exact --nocapture #[test] fn test_do_revoke_children_multiple_network_does_not_exist() { @@ -884,6 +1246,11 @@ fn test_do_revoke_children_multiple_network_does_not_exist() { }); } +// 30: Test revoking children with non-associated coldkey +// This test verifies the behavior when attempting to revoke children using a non-associated coldkey: +// - Adds a network and registers a hotkey with a different coldkey +// - Attempts to revoke children using an unassociated coldkey +// - Verifies that the operation fails with the correct error // SKIP_WASM_BUILD=1 RUST_LOG=info cargo test --test children -- test_do_revoke_children_multiple_non_associated_coldkey --exact --nocapture #[test] fn test_do_revoke_children_multiple_non_associated_coldkey() { @@ -911,6 +1278,13 @@ fn test_do_revoke_children_multiple_non_associated_coldkey() { }); } +// 31: Test partial revocation of children +// This test verifies the behavior when partially revoking children: +// - Adds a network and registers a hotkey +// - Sets multiple children for the hotkey +// - Revokes one of the children +// - Verifies that the correct children remain and the revoked child is removed +// - Checks the parent-child relationships after partial revocation // SKIP_WASM_BUILD=1 RUST_LOG=info cargo test --test children -- test_do_revoke_children_multiple_partial_revocation --exact --nocapture #[test] fn test_do_revoke_children_multiple_partial_revocation() { @@ -961,8 +1335,14 @@ fn test_do_revoke_children_multiple_partial_revocation() { }); } +// 32: Test revoking non-existent children +// This test verifies the behavior when attempting to revoke non-existent children: +// - Adds a network and registers a hotkey +// - Sets one child for the hotkey +// - Attempts to revoke all children (including non-existent ones) +// - Verifies that all children are removed, including the existing one +// - Checks that the parent-child relationship is properly updated // SKIP_WASM_BUILD=1 RUST_LOG=info cargo test --test children -- test_do_revoke_children_multiple_non_existent_children --exact --nocapture - #[test] fn test_do_revoke_children_multiple_non_existent_children() { new_test_ext(1).execute_with(|| { @@ -1002,6 +1382,11 @@ fn test_do_revoke_children_multiple_non_existent_children() { }); } +// 33: Test revoking children with an empty list +// This test verifies the behavior when attempting to revoke children using an empty list: +// - Adds a network and registers a hotkey +// - Attempts to revoke children with an empty list +// - Verifies that no changes occur in the children list // SKIP_WASM_BUILD=1 RUST_LOG=info cargo test --test children -- test_do_revoke_children_multiple_empty_list --exact --nocapture #[test] fn test_do_revoke_children_multiple_empty_list() { @@ -1028,6 +1413,13 @@ fn test_do_revoke_children_multiple_empty_list() { }); } +// 34: Test complex scenario for revoking multiple children +// This test verifies a complex scenario involving setting and revoking multiple children: +// - Adds a network and registers a hotkey +// - Sets multiple children with different proportions +// - Revokes one child and verifies the remaining children +// - Revokes all remaining children +// - Verifies that all parent-child relationships are properly updated // SKIP_WASM_BUILD=1 RUST_LOG=info cargo test --test children -- test_do_revoke_children_multiple_complex_scenario --exact --nocapture #[test] fn test_do_revoke_children_multiple_complex_scenario() { @@ -1094,6 +1486,11 @@ fn test_do_revoke_children_multiple_complex_scenario() { }); } +// 35: Test getting network max stake +// This test verifies the functionality of getting the network max stake: +// - Checks the default max stake value +// - Sets a new max stake value +// - Verifies that the new value is retrieved correctly // SKIP_WASM_BUILD=1 RUST_LOG=info cargo test --test children -- test_get_network_max_stake --exact --nocapture #[test] fn test_get_network_max_stake() { @@ -1102,7 +1499,7 @@ fn test_get_network_max_stake() { let default_max_stake = SubtensorModule::get_network_max_stake(netuid); // Check that the default value is set correctly - assert_eq!(default_max_stake, 500_000_000_000_000); + assert_eq!(default_max_stake, u64::MAX); // Set a new max stake value let new_max_stake: u64 = 1_000_000; @@ -1116,6 +1513,12 @@ fn test_get_network_max_stake() { }); } +// 36: Test setting network max stake +// This test verifies the functionality of setting the network max stake: +// - Checks the initial max stake value +// - Sets a new max stake value +// - Verifies that the new value is set correctly +// - Checks that the appropriate event is emitted // SKIP_WASM_BUILD=1 RUST_LOG=info cargo test --test children -- test_set_network_max_stake --exact --nocapture #[test] fn test_set_network_max_stake() { @@ -1142,6 +1545,11 @@ fn test_set_network_max_stake() { }); } +// 37: Test setting network max stake for multiple networks +// This test verifies the functionality of setting different max stake values for multiple networks: +// - Sets different max stake values for two networks +// - Verifies that the values are set correctly for each network +// - Checks that the values are different between networks // SKIP_WASM_BUILD=1 RUST_LOG=info cargo test --test children -- test_set_network_max_stake_multiple_networks --exact --nocapture #[test] fn test_set_network_max_stake_multiple_networks() { @@ -1165,6 +1573,12 @@ fn test_set_network_max_stake_multiple_networks() { }); } +// 38: Test updating network max stake +// This test verifies the functionality of updating an existing network max stake value: +// - Sets an initial max stake value +// - Updates the max stake value +// - Verifies that the value is updated correctly +// - Checks that the appropriate event is emitted for the update // SKIP_WASM_BUILD=1 RUST_LOG=info cargo test --test children -- test_set_network_max_stake_update --exact --nocapture #[test] fn test_set_network_max_stake_update() { @@ -1194,6 +1608,13 @@ fn test_set_network_max_stake_update() { }); } +// 39: Test children stake values +// This test verifies the correct distribution of stake among parent and child neurons: +// - Sets up a network with a parent neuron and multiple child neurons +// - Assigns stake to the parent neuron +// - Sets child neurons with specific proportions +// - Verifies that the stake is correctly distributed among parent and child neurons +// - Checks that the total stake remains constant across all neurons // SKIP_WASM_BUILD=1 RUST_LOG=info cargo test --test children -- test_children_stake_values --exact --nocapture #[test] fn test_children_stake_values() { @@ -1258,3 +1679,1561 @@ fn test_children_stake_values() { ); }); } + +// 40: Test getting parents chain +// This test verifies the correct implementation of parent-child relationships and the get_parents function: +// - Sets up a network with multiple neurons in a chain of parent-child relationships +// - Verifies that each neuron has the correct parent +// - Tests the root neuron has no parents +// - Tests a neuron with multiple parents +// - Verifies correct behavior when adding a new parent to an existing child +// SKIP_WASM_BUILD=1 RUST_LOG=info cargo test --test children -- test_get_parents_chain --exact --nocapture +#[test] +fn test_get_parents_chain() { + new_test_ext(1).execute_with(|| { + let netuid: u16 = 1; + let coldkey = U256::from(1); + let num_keys: usize = 5; + let proportion = u64::MAX / 2; // 50% stake allocation + + log::info!( + "Test setup: netuid={}, coldkey={}, num_keys={}, proportion={}", + netuid, + coldkey, + num_keys, + proportion + ); + + // Create a vector of hotkeys + let hotkeys: Vec = (0..num_keys).map(|i| U256::from(i as u64 + 2)).collect(); + log::info!("Created hotkeys: {:?}", hotkeys); + + // Add network + add_network(netuid, 13, 0); + SubtensorModule::set_max_registrations_per_block(netuid, 1000); + SubtensorModule::set_target_registrations_per_interval(netuid, 1000); + log::info!("Network added and parameters set: netuid={}", netuid); + + // Register all neurons + for hotkey in &hotkeys { + register_ok_neuron(netuid, *hotkey, coldkey, 0); + log::info!( + "Registered neuron: hotkey={}, coldkey={}, netuid={}", + hotkey, + coldkey, + netuid + ); + } + + // Set up parent-child relationships + for i in 0..num_keys - 1 { + assert_ok!(SubtensorModule::do_set_children( + RuntimeOrigin::signed(coldkey), + hotkeys[i], + netuid, + vec![(proportion, hotkeys[i + 1])] + )); + log::info!( + "Set parent-child relationship: parent={}, child={}, proportion={}", + hotkeys[i], + hotkeys[i + 1], + proportion + ); + } + + // Test get_parents for each hotkey + for i in 1..num_keys { + let parents = SubtensorModule::get_parents(&hotkeys[i], netuid); + log::info!( + "Testing get_parents for hotkey {}: {:?}", + hotkeys[i], + parents + ); + assert_eq!( + parents.len(), + 1, + "Hotkey {} should have exactly one parent", + i + ); + assert_eq!( + parents[0], + (proportion, hotkeys[i - 1]), + "Incorrect parent for hotkey {}", + i + ); + } + + // Test get_parents for the root (should be empty) + let root_parents = SubtensorModule::get_parents(&hotkeys[0], netuid); + log::info!( + "Testing get_parents for root hotkey {}: {:?}", + hotkeys[0], + root_parents + ); + assert!( + root_parents.is_empty(), + "Root hotkey should have no parents" + ); + + // Test multiple parents + let last_hotkey = hotkeys[num_keys - 1]; + let new_parent = U256::from(num_keys as u64 + 2); + register_ok_neuron(netuid, new_parent, coldkey, 0); + log::info!( + "Registered new parent neuron: new_parent={}, coldkey={}, netuid={}", + new_parent, + coldkey, + netuid + ); + + assert_ok!(SubtensorModule::do_set_children( + RuntimeOrigin::signed(coldkey), + new_parent, + netuid, + vec![(proportion / 2, last_hotkey)] + )); + log::info!( + "Set additional parent-child relationship: parent={}, child={}, proportion={}", + new_parent, + last_hotkey, + proportion / 2 + ); + + let last_hotkey_parents = SubtensorModule::get_parents(&last_hotkey, netuid); + log::info!( + "Testing get_parents for last hotkey {} with multiple parents: {:?}", + last_hotkey, + last_hotkey_parents + ); + assert_eq!( + last_hotkey_parents.len(), + 2, + "Last hotkey should have two parents" + ); + assert!( + last_hotkey_parents.contains(&(proportion, hotkeys[num_keys - 2])), + "Last hotkey should still have its original parent" + ); + assert!( + last_hotkey_parents.contains(&(proportion / 2, new_parent)), + "Last hotkey should have the new parent" + ); + }); +} + +// 41: Test emission distribution between a childkey and a single parent +// This test verifies the correct distribution of emissions between a child and a single parent: +// - Sets up a network with a parent, child, and weight setter +// - Establishes a parent-child relationship +// - Sets weights on the child +// - Runs an epoch with a hardcoded emission value +// - Checks the emission distribution among parent, child, and weight setter +// - Verifies that all parties received emissions and the weight setter received the most +// SKIP_WASM_BUILD=1 RUST_LOG=debug cargo test --test children test_childkey_single_parent_emission -- --nocapture +#[test] +fn test_childkey_single_parent_emission() { + new_test_ext(1).execute_with(|| { + let netuid: u16 = 1; + add_network(netuid, 1, 0); + + // Define hotkeys + let parent: U256 = U256::from(1); + let child: U256 = U256::from(2); + let weight_setter: U256 = U256::from(3); + + // Define coldkeys with more readable names + let coldkey_parent: U256 = U256::from(100); + let coldkey_child: U256 = U256::from(101); + let coldkey_weight_setter: U256 = U256::from(102); + + // Register parent with minimal stake and child with high stake + SubtensorModule::add_balance_to_coldkey_account(&coldkey_parent, 1); + SubtensorModule::add_balance_to_coldkey_account(&coldkey_child, 109_999); + SubtensorModule::add_balance_to_coldkey_account(&coldkey_weight_setter, 1_000_000); + + // Add neurons for parent, child and weight_setter + register_ok_neuron(netuid, parent, coldkey_parent, 1); + register_ok_neuron(netuid, child, coldkey_child, 1); + register_ok_neuron(netuid, weight_setter, coldkey_weight_setter, 1); + + SubtensorModule::increase_stake_on_coldkey_hotkey_account( + &coldkey_parent, + &parent, + 109_999, + ); + SubtensorModule::increase_stake_on_coldkey_hotkey_account( + &coldkey_weight_setter, + &weight_setter, + 1_000_000, + ); + + SubtensorModule::set_weights_set_rate_limit(netuid, 0); + + // Set parent-child relationship + assert_ok!(SubtensorModule::do_set_children( + RuntimeOrigin::signed(coldkey_parent), + parent, + netuid, + vec![(u64::MAX, child)] + )); + step_block(7200 + 1); + // Set weights on the child using the weight_setter account + let origin = RuntimeOrigin::signed(weight_setter); + let uids: Vec = vec![1]; // Only set weight for the child (UID 1) + let values: Vec = vec![u16::MAX]; // Use maximum value for u16 + let version_key = SubtensorModule::get_weights_version_key(netuid); + assert_ok!(SubtensorModule::set_weights( + origin, + netuid, + uids, + values, + version_key + )); + + // Run epoch with a hardcoded emission value + let hardcoded_emission: u64 = 1_000_000_000; // 1 TAO + let hotkey_emission: Vec<(U256, u64, u64)> = + SubtensorModule::epoch(netuid, hardcoded_emission); + + // Process the hotkey emission results + for (hotkey, mining_emission, validator_emission) in hotkey_emission { + SubtensorModule::accumulate_hotkey_emission( + &hotkey, + netuid, + validator_emission, + mining_emission, + ); + log::debug!( + "Accumulated emissions on hotkey {:?} for netuid {:?}: mining {:?}, validator {:?}", + hotkey, + netuid, + mining_emission, + validator_emission + ); + } + step_block(7200 + 1); + // Check emission distribution + let parent_stake: u64 = + SubtensorModule::get_stake_for_coldkey_and_hotkey(&coldkey_parent, &parent); + let parent_stake_on_subnet: u64 = + SubtensorModule::get_stake_for_hotkey_on_subnet(&parent, netuid); + + log::debug!( + "Parent stake: {:?}, Parent stake on subnet: {:?}", + parent_stake, + parent_stake_on_subnet + ); + + let child_stake: u64 = + SubtensorModule::get_stake_for_coldkey_and_hotkey(&coldkey_child, &child); + let child_stake_on_subnet: u64 = + SubtensorModule::get_stake_for_hotkey_on_subnet(&child, netuid); + + log::debug!( + "Child stake: {:?}, Child stake on subnet: {:?}", + child_stake, + child_stake_on_subnet + ); + + let weight_setter_stake: u64 = SubtensorModule::get_stake_for_coldkey_and_hotkey( + &coldkey_weight_setter, + &weight_setter, + ); + let weight_setter_stake_on_subnet: u64 = + SubtensorModule::get_stake_for_hotkey_on_subnet(&weight_setter, netuid); + + log::debug!( + "Weight setter stake: {:?}, Weight setter stake on subnet: {:?}", + weight_setter_stake, + weight_setter_stake_on_subnet + ); + + assert!(parent_stake > 1, "Parent should have received emission"); + assert!(child_stake > 109_999, "Child should have received emission"); + assert!( + weight_setter_stake > 1_000_000, + "Weight setter should have received emission" + ); + + // Additional assertion to verify that the weight setter received the most emission + assert!( + weight_setter_stake > parent_stake && weight_setter_stake > child_stake, + "Weight setter should have received the most emission" + ); + }); +} + +// 43: Test emission distribution between a childkey and multiple parents +// This test verifies the correct distribution of emissions between a child and multiple parents: +// - Sets up a network with two parents, a child, and a weight setter +// - Establishes parent-child relationships with different stake proportions +// - Sets weights on the child and one parent +// - Runs an epoch with a hardcoded emission value +// - Checks the emission distribution among parents, child, and weight setter +// - Verifies that all parties received emissions and the total stake increased correctly +// SKIP_WASM_BUILD=1 RUST_LOG=debug cargo test --test coinbase test_childkey_multiple_parents_emission -- --nocapture +#[test] +fn test_childkey_multiple_parents_emission() { + new_test_ext(1).execute_with(|| { + let netuid: u16 = 1; + add_network(netuid, 1, 0); + + // Set registration parameters and emission tempo + SubtensorModule::set_max_registrations_per_block(netuid, 1000); + SubtensorModule::set_target_registrations_per_interval(netuid, 1000); + SubtensorModule::set_hotkey_emission_tempo(10); + + // Define hotkeys and coldkeys + let parent1: U256 = U256::from(1); + let parent2: U256 = U256::from(2); + let child: U256 = U256::from(3); + let weight_setter: U256 = U256::from(4); + let coldkey_parent1: U256 = U256::from(100); + let coldkey_parent2: U256 = U256::from(101); + let coldkey_child: U256 = U256::from(102); + let coldkey_weight_setter: U256 = U256::from(103); + + // Register neurons and add initial stakes + let initial_stakes: Vec<(U256, U256, u64)> = vec![ + (coldkey_parent1, parent1, 200_000), + (coldkey_parent2, parent2, 150_000), + (coldkey_child, child, 20_000), + (coldkey_weight_setter, weight_setter, 100_000), + ]; + + for (coldkey, hotkey, stake) in initial_stakes.iter() { + SubtensorModule::add_balance_to_coldkey_account(coldkey, *stake); + register_ok_neuron(netuid, *hotkey, *coldkey, 0); + SubtensorModule::increase_stake_on_coldkey_hotkey_account(coldkey, hotkey, *stake); + } + + SubtensorModule::set_weights_set_rate_limit(netuid, 0); + step_block(2); + + // Set parent-child relationships + assert_ok!(SubtensorModule::do_set_children( + RuntimeOrigin::signed(coldkey_parent1), + parent1, + netuid, + vec![(100_000, child)] + )); + assert_ok!(SubtensorModule::do_set_children( + RuntimeOrigin::signed(coldkey_parent2), + parent2, + netuid, + vec![(75_000, child)] + )); + + // Set weights + let uids: Vec = vec![0, 1, 2]; + let values: Vec = vec![0, 65354, 65354]; + let version_key = SubtensorModule::get_weights_version_key(netuid); + assert_ok!(SubtensorModule::set_weights( + RuntimeOrigin::signed(weight_setter), + netuid, + uids, + values, + version_key + )); + + // Run epoch with a hardcoded emission value + let hardcoded_emission: u64 = 1_000_000_000; // 1 billion + let hotkey_emission: Vec<(U256, u64, u64)> = + SubtensorModule::epoch(netuid, hardcoded_emission); + + // Process the hotkey emission results + for (hotkey, mining_emission, validator_emission) in hotkey_emission { + SubtensorModule::accumulate_hotkey_emission( + &hotkey, + netuid, + validator_emission, + mining_emission, + ); + log::debug!( + "Accumulated emissions on hotkey {:?} for netuid {:?}: mining {:?}, validator {:?}", + hotkey, + netuid, + mining_emission, + validator_emission + ); + } + + step_block(11); + + // Check emission distribution + let stakes: Vec<(U256, U256, &str)> = vec![ + (coldkey_parent1, parent1, "Parent1"), + (coldkey_parent2, parent2, "Parent2"), + (coldkey_child, child, "Child"), + (coldkey_weight_setter, weight_setter, "Weight setter"), + ]; + + for (coldkey, hotkey, name) in stakes.iter() { + let stake = SubtensorModule::get_stake_for_coldkey_and_hotkey(coldkey, hotkey); + let stake_on_subnet = SubtensorModule::get_stake_for_hotkey_on_subnet(hotkey, netuid); + log::debug!( + "{} stake: {:?}, {} stake on subnet: {:?}", + name, + stake, + name, + stake_on_subnet + ); + } + + let parent1_stake = + SubtensorModule::get_stake_for_coldkey_and_hotkey(&coldkey_parent1, &parent1); + let parent2_stake = + SubtensorModule::get_stake_for_coldkey_and_hotkey(&coldkey_parent2, &parent2); + let child_stake = SubtensorModule::get_stake_for_coldkey_and_hotkey(&coldkey_child, &child); + let weight_setter_stake = SubtensorModule::get_stake_for_coldkey_and_hotkey( + &coldkey_weight_setter, + &weight_setter, + ); + + assert!( + parent1_stake > 200_000, + "Parent1 should have received emission" + ); + assert!( + parent2_stake > 150_000, + "Parent2 should have received emission" + ); + assert!(child_stake > 20_000, "Child should have received emission"); + assert!( + weight_setter_stake > 100_000, + "Weight setter should have received emission" + ); + + // Check individual stake increases + let parent1_stake_increase = parent1_stake - 200_000; + let parent2_stake_increase = parent2_stake - 150_000; + let child_stake_increase = child_stake - 20_000; + + log::debug!( + "Stake increases - Parent1: {}, Parent2: {}, Child: {}", + parent1_stake_increase, + parent2_stake_increase, + child_stake_increase + ); + + // Assert that all neurons received some emission + assert!( + parent1_stake_increase > 0, + "Parent1 should have received some emission" + ); + assert!( + parent2_stake_increase > 0, + "Parent2 should have received some emission" + ); + assert!( + child_stake_increase > 0, + "Child should have received some emission" + ); + + // Check that the total stake has increased by the hardcoded emission amount + let total_stake = parent1_stake + parent2_stake + child_stake + weight_setter_stake; + let initial_total_stake: u64 = initial_stakes.iter().map(|(_, _, stake)| stake).sum(); + assert_eq!( + total_stake, + initial_total_stake + hardcoded_emission - 2, // U64::MAX normalization rounding error + "Total stake should have increased by the hardcoded emission amount" + ); + }); +} + +// 44: Test with a chain of parent-child relationships (e.g., A -> B -> C) +// This test verifies the correct distribution of emissions in a chain of parent-child relationships: +// - Sets up a network with three neurons A, B, and C in a chain (A -> B -> C) +// - Establishes parent-child relationships with different stake proportions +// - Sets weights for all neurons +// - Runs an epoch with a hardcoded emission value +// - Checks the emission distribution among A, B, and C +// - Verifies that all parties received emissions and the total stake increased correctly +// SKIP_WASM_BUILD=1 RUST_LOG=debug cargo test --test coinbase test_parent_child_chain_emission -- --nocapture +#[test] +fn test_parent_child_chain_emission() { + new_test_ext(1).execute_with(|| { + let netuid: u16 = 1; + add_network(netuid, 1, 0); + + // Define hotkeys and coldkeys + let hotkey_a: U256 = U256::from(1); + let hotkey_b: U256 = U256::from(2); + let hotkey_c: U256 = U256::from(3); + let coldkey_a: U256 = U256::from(100); + let coldkey_b: U256 = U256::from(101); + let coldkey_c: U256 = U256::from(102); + + // Register neurons with decreasing stakes + register_ok_neuron(netuid, hotkey_a, coldkey_a, 0); + register_ok_neuron(netuid, hotkey_b, coldkey_b, 0); + register_ok_neuron(netuid, hotkey_c, coldkey_c, 0); + + // Add initial stakes + SubtensorModule::add_balance_to_coldkey_account(&coldkey_a, 300_000); + SubtensorModule::add_balance_to_coldkey_account(&coldkey_b, 100_000); + SubtensorModule::add_balance_to_coldkey_account(&coldkey_c, 50_000); + + SubtensorModule::increase_stake_on_coldkey_hotkey_account(&coldkey_a, &hotkey_a, 300_000); + SubtensorModule::increase_stake_on_coldkey_hotkey_account(&coldkey_b, &hotkey_b, 100_000); + SubtensorModule::increase_stake_on_coldkey_hotkey_account(&coldkey_c, &hotkey_c, 50_000); + + // Set parent-child relationships + // A -> B (50% of A's stake) + assert_ok!(SubtensorModule::do_set_children( + RuntimeOrigin::signed(coldkey_a), + hotkey_a, + netuid, + vec![(u64::MAX / 2, hotkey_b)] + )); + // B -> C (50% of B's stake) + assert_ok!(SubtensorModule::do_set_children( + RuntimeOrigin::signed(coldkey_b), + hotkey_b, + netuid, + vec![(u64::MAX / 2, hotkey_c)] + )); + + step_block(2); + + // Set weights + let origin = RuntimeOrigin::signed(hotkey_a); + let uids: Vec = vec![0, 1, 2]; // UIDs for hotkey_a, hotkey_b, hotkey_c + let values: Vec = vec![65535, 65535, 65535]; // Set equal weights for all hotkeys + let version_key = SubtensorModule::get_weights_version_key(netuid); + + // Ensure we can set weights without rate limiting + SubtensorModule::set_weights_set_rate_limit(netuid, 0); + + assert_ok!(SubtensorModule::set_weights( + origin, + netuid, + uids, + values, + version_key + )); + + // Run epoch with a hardcoded emission value + let hardcoded_emission: u64 = 1_000_000; // 1 million (adjust as needed) + let hotkey_emission: Vec<(U256, u64, u64)> = + SubtensorModule::epoch(netuid, hardcoded_emission); + + // Process the hotkey emission results + for (hotkey, mining_emission, validator_emission) in hotkey_emission { + SubtensorModule::accumulate_hotkey_emission( + &hotkey, + netuid, + validator_emission, + mining_emission, + ); + } + + // Log PendingEmission Tuple for a, b, c + let pending_emission_a = SubtensorModule::get_pending_hotkey_emission(&hotkey_a); + let pending_emission_b = SubtensorModule::get_pending_hotkey_emission(&hotkey_b); + let pending_emission_c = SubtensorModule::get_pending_hotkey_emission(&hotkey_c); + + log::info!("Pending Emission for A: {:?}", pending_emission_a); + log::info!("Pending Emission for B: {:?}", pending_emission_b); + log::info!("Pending Emission for C: {:?}", pending_emission_c); + + // Assert that pending emissions are non-zero + // A's pending emission: 2/3 of total emission (due to having 2/3 of total stake) + assert!( + pending_emission_a == 666667, + "A should have pending emission of 2/3 of total emission" + ); + // B's pending emission: 2/9 of total emission (1/3 of A's emission + 1/3 of total emission) + assert!( + pending_emission_b == 222222, + "B should have pending emission of 2/9 of total emission" + ); + // C's pending emission: 1/9 of total emission (1/2 of B's emission) + assert!( + pending_emission_c == 111109, + "C should have pending emission of 1/9 of total emission" + ); + + SubtensorModule::set_hotkey_emission_tempo(10); + + step_block(10 + 1); + // Retrieve the current stake for each hotkey on the subnet + let stake_a: u64 = SubtensorModule::get_stake_for_hotkey_on_subnet(&hotkey_a, netuid); + let stake_b: u64 = SubtensorModule::get_stake_for_hotkey_on_subnet(&hotkey_b, netuid); + let stake_c: u64 = SubtensorModule::get_stake_for_hotkey_on_subnet(&hotkey_c, netuid); + + // Log the current stakes for debugging purposes + log::info!("Stake for hotkey A: {:?}", stake_a); + log::info!("Stake for hotkey B: {:?}", stake_b); + log::info!("Stake for hotkey C: {:?}", stake_c); + + // Assert that the stakes have been updated correctly after emission distribution + assert_eq!( + stake_a, 483334, + "A's stake should be 483334 (initial 300_000 + 666667 emission - 483333 given to B)" + ); + assert_eq!( + stake_b, 644445, + "B's stake should be 644445 (initial 100_000 + 222222 emission + 483333 from A - 161110 given to C)" + ); + assert_eq!( + stake_c, 322219, + "C's stake should be 322219 (initial 50_000 + 111109 emission + 161110 from B)" + ); + + // Check that the total stake has increased by the hardcoded emission amount + let total_stake = stake_a + stake_b + stake_c; + let initial_total_stake = 300_000 + 100_000 + 50_000; + let hardcoded_emission = 1_000_000; // Define the hardcoded emission value + assert_eq!( + total_stake, + initial_total_stake + hardcoded_emission - 2, // U64::MAX normalization rounding error + "Total stake should have increased by the hardcoded emission amount" + ); + }); +} + +// 46: Test emission distribution when adding/removing parent-child relationships mid-epoch +// This test verifies the correct distribution of emissions when parent-child relationships change: +// - Sets up a network with three neurons: parent, child1, and child2 +// - Establishes initial parent-child relationship between parent and child1 +// - Runs first epoch and distributes emissions +// - Changes parent-child relationships to include both child1 and child2 +// - Runs second epoch and distributes emissions +// - Checks final emission distribution and stake updates +// - Verifies correct parent-child relationships and stake proportions +// SKIP_WASM_BUILD=1 RUST_LOG=debug cargo test --test children -- test_dynamic_parent_child_relationships --exact --nocapture +#[test] +fn test_dynamic_parent_child_relationships() { + new_test_ext(1).execute_with(|| { + let netuid: u16 = 1; + add_network(netuid, 1, 0); + + // Define hotkeys and coldkeys + let parent: U256 = U256::from(1); + let child1: U256 = U256::from(2); + let child2: U256 = U256::from(3); + let coldkey_parent: U256 = U256::from(100); + let coldkey_child1: U256 = U256::from(101); + let coldkey_child2: U256 = U256::from(102); + + // Register neurons with varying stakes + register_ok_neuron(netuid, parent, coldkey_parent, 0); + register_ok_neuron(netuid, child1, coldkey_child1, 0); + register_ok_neuron(netuid, child2, coldkey_child2, 0); + + // Add initial stakes + SubtensorModule::add_balance_to_coldkey_account(&coldkey_parent, 500_000); + SubtensorModule::add_balance_to_coldkey_account(&coldkey_child1, 50_000); + SubtensorModule::add_balance_to_coldkey_account(&coldkey_child2, 30_000); + + SubtensorModule::increase_stake_on_coldkey_hotkey_account(&coldkey_parent, &parent, 500_000); + SubtensorModule::increase_stake_on_coldkey_hotkey_account(&coldkey_child1, &child1, 50_000); + SubtensorModule::increase_stake_on_coldkey_hotkey_account(&coldkey_child2, &child2, 30_000); + + // Set initial parent-child relationship + assert_ok!(SubtensorModule::do_set_children( + RuntimeOrigin::signed(coldkey_parent), + parent, + netuid, + vec![(u64::MAX / 2, child1)] + )); + + step_block(2); + + // Set weights + let origin = RuntimeOrigin::signed(parent); + let uids: Vec = vec![0, 1, 2]; // UIDs for parent, child1, child2 + let values: Vec = vec![65535, 65535, 65535]; // Set equal weights for all hotkeys + let version_key = SubtensorModule::get_weights_version_key(netuid); + + // Ensure we can set weights without rate limiting + SubtensorModule::set_weights_set_rate_limit(netuid, 0); + + assert_ok!(SubtensorModule::set_weights( + origin, + netuid, + uids, + values, + version_key + )); + + // Set hotkey emission tempo + SubtensorModule::set_hotkey_emission_tempo(10); + + // Run first epoch + let hardcoded_emission: u64 = 1_000_000; // 1 million (adjust as needed) + let hotkey_emission: Vec<(U256, u64, u64)> = SubtensorModule::epoch(netuid, hardcoded_emission); + + // Process the hotkey emission results + for (hotkey, mining_emission, validator_emission) in hotkey_emission { + SubtensorModule::accumulate_hotkey_emission(&hotkey, netuid, validator_emission, mining_emission); + } + + // Step blocks to allow for emission distribution + step_block(11); + + // Change parent-child relationships + assert_ok!(SubtensorModule::do_set_children( + RuntimeOrigin::signed(coldkey_parent), + parent, + netuid, + vec![(u64::MAX / 4, child1), (u64::MAX / 3, child2)] + )); + + // Run second epoch + let hotkey_emission: Vec<(U256, u64, u64)> = SubtensorModule::epoch(netuid, hardcoded_emission); + + // Process the hotkey emission results + for (hotkey, mining_emission, validator_emission) in hotkey_emission { + SubtensorModule::accumulate_hotkey_emission(&hotkey, netuid, validator_emission, mining_emission); + } + + // Step blocks again to allow for emission distribution + step_block(11); + + // Check final emission distribution + let parent_stake: u64 = SubtensorModule::get_stake_for_hotkey_on_subnet(&parent, netuid); + let child1_stake: u64 = SubtensorModule::get_stake_for_hotkey_on_subnet(&child1, netuid); + let child2_stake: u64 = SubtensorModule::get_stake_for_hotkey_on_subnet(&child2, netuid); + + log::info!("Final stakes:"); + log::info!("Parent stake: {}", parent_stake); + log::info!("Child1 stake: {}", child1_stake); + log::info!("Child2 stake: {}", child2_stake); + + const TOLERANCE: u64 = 5; // Allow for a small discrepancy due to potential rounding + + // Precise assertions with tolerance + assert!( + (parent_stake as i64 - 926725).abs() <= TOLERANCE as i64, + "Parent stake should be close to 926,725, but was {}", + parent_stake + ); + // Parent stake calculation: + // Initial stake: 500,000 + // First epoch: ~862,500 (500,000 + 725,000 * 1/2) + // Second epoch: ~926,725 (862,500 + 725,000 * 5/12) + + assert!( + (child1_stake as i64 - 778446).abs() <= TOLERANCE as i64, + "Child1 stake should be close to 778,446, but was {}", + child1_stake + ); + // Child1 stake calculation: + // Initial stake: 50,000 + // First epoch: ~412,500 (50,000 + 725,000 * 1/2) + // Second epoch: ~778,446 (412,500 + 725,000 * 1/2 * 1/4 + 137,500) + + assert!( + (child2_stake as i64 - 874826).abs() <= TOLERANCE as i64, + "Child2 stake should be close to 874,826, but was {}", + child2_stake + ); + // Child2 stake calculation: + // Initial stake: 30,000 + // First epoch: ~167,500 (30,000 + 137,500) + // Second epoch: ~874,826 (167,500 + 725,000 * 1/2 * 1/3 + 137,500) + + // Check that the total stake has increased by approximately twice the hardcoded emission amount + let total_stake: u64 = parent_stake + child1_stake + child2_stake; + let initial_total_stake: u64 = 500_000 + 50_000 + 30_000; + let total_emission: u64 = 2 * hardcoded_emission; + assert!( + (total_stake as i64 - (initial_total_stake + total_emission) as i64).abs() <= TOLERANCE as i64, + "Total stake should have increased by approximately twice the hardcoded emission amount" + ); + // Total stake calculation: + // Initial total stake: 500,000 + 50,000 + 30,000 = 580,000 + // Total emission: 2 * 1,000,000 = 2,000,000 + // Expected total stake: 580,000 + 2,000,000 = 2,580,000 + + // Additional checks for parent-child relationships + let parent_children: Vec<(u64, U256)> = SubtensorModule::get_children(&parent, netuid); + assert_eq!( + parent_children, + vec![(u64::MAX / 4, child1), (u64::MAX / 3, child2)], + "Parent should have both children with correct proportions" + ); + // Parent-child relationship: + // child1: 1/4 of parent's stake + // child2: 1/3 of parent's stake + + let child1_parents: Vec<(u64, U256)> = SubtensorModule::get_parents(&child1, netuid); + assert_eq!( + child1_parents, + vec![(u64::MAX / 4, parent)], + "Child1 should have parent as its parent with correct proportion" + ); + // Child1-parent relationship: + // parent: 1/4 of child1's stake + + let child2_parents: Vec<(u64, U256)> = SubtensorModule::get_parents(&child2, netuid); + assert_eq!( + child2_parents, + vec![(u64::MAX / 3, parent)], + "Child2 should have parent as its parent with correct proportion" + ); + // Child2-parent relationship: + // parent: 1/3 of child2's stake + + // Check that child2 has received more stake than child1 + assert!( + child2_stake > child1_stake, + "Child2 should have received more emission than Child1 due to higher proportion" + ); + // Child2 stake (874,826) > Child1 stake (778,446) + + // Check the approximate difference between child2 and child1 stakes + let stake_difference: u64 = child2_stake - child1_stake; + assert!( + (stake_difference as i64 - 96_380).abs() <= TOLERANCE as i64, + "The difference between Child2 and Child1 stakes should be close to 96,380, but was {}", + stake_difference + ); + // Stake difference calculation: + // Child2 stake: 874,826 + // Child1 stake: 778,446 + // Difference: 874,826 - 778,446 = 96,380 + }); +} + +// 47: Test basic stake retrieval for a single hotkey on a subnet +/// This test verifies the basic functionality of retrieving stake for a single hotkey on a subnet: +/// - Sets up a network with one neuron +/// - Increases stake for the neuron +/// - Checks if the retrieved stake matches the increased amount +/// SKIP_WASM_BUILD=1 RUST_LOG=debug cargo test --test children -- test_get_stake_for_hotkey_on_subnet_basic --exact --nocapture +#[test] +fn test_get_stake_for_hotkey_on_subnet_basic() { + new_test_ext(1).execute_with(|| { + let netuid: u16 = 1; + let hotkey = U256::from(1); + let coldkey = U256::from(2); + + add_network(netuid, 0, 0); + register_ok_neuron(netuid, hotkey, coldkey, 0); + + SubtensorModule::increase_stake_on_coldkey_hotkey_account(&coldkey, &hotkey, 1000); + + assert_eq!( + SubtensorModule::get_stake_for_hotkey_on_subnet(&hotkey, netuid), + 1000 + ); + }); +} + +// 48: Test stake retrieval for a hotkey with multiple coldkeys on a subnet +/// This test verifies the functionality of retrieving stake for a hotkey with multiple coldkeys on a subnet: +/// - Sets up a network with one neuron and two coldkeys +/// - Increases stake from both coldkeys +/// - Checks if the retrieved stake matches the total increased amount +/// SKIP_WASM_BUILD=1 RUST_LOG=debug cargo test --test children -- test_get_stake_for_hotkey_on_subnet_multiple_coldkeys --exact --nocapture +#[test] +fn test_get_stake_for_hotkey_on_subnet_multiple_coldkeys() { + new_test_ext(1).execute_with(|| { + let netuid: u16 = 1; + let hotkey = U256::from(1); + let coldkey1 = U256::from(2); + let coldkey2 = U256::from(3); + + add_network(netuid, 0, 0); + register_ok_neuron(netuid, hotkey, coldkey1, 0); + + SubtensorModule::increase_stake_on_coldkey_hotkey_account(&coldkey1, &hotkey, 1000); + SubtensorModule::increase_stake_on_coldkey_hotkey_account(&coldkey2, &hotkey, 2000); + + assert_eq!( + SubtensorModule::get_stake_for_hotkey_on_subnet(&hotkey, netuid), + 3000 + ); + }); +} + +// 49: Test stake retrieval for a single parent-child relationship on a subnet +/// This test verifies the functionality of retrieving stake for a single parent-child relationship on a subnet: +/// - Sets up a network with a parent and child neuron +/// - Increases stake for the parent +/// - Sets the child as the parent's only child with 100% stake allocation +/// - Checks if the retrieved stake for both parent and child is correct +/// SKIP_WASM_BUILD=1 RUST_LOG=debug cargo test --test children -- test_get_stake_for_hotkey_on_subnet_single_parent_child --exact --nocapture +#[test] +fn test_get_stake_for_hotkey_on_subnet_single_parent_child() { + new_test_ext(1).execute_with(|| { + let netuid: u16 = 1; + let parent = U256::from(1); + let child = U256::from(2); + let coldkey = U256::from(3); + + add_network(netuid, 0, 0); + register_ok_neuron(netuid, parent, coldkey, 0); + register_ok_neuron(netuid, child, coldkey, 0); + + SubtensorModule::increase_stake_on_coldkey_hotkey_account(&coldkey, &parent, 1000); + + assert_ok!(SubtensorModule::do_set_children( + RuntimeOrigin::signed(coldkey), + parent, + netuid, + vec![(u64::MAX, child)] + )); + + assert_eq!( + SubtensorModule::get_stake_for_hotkey_on_subnet(&parent, netuid), + 0 + ); + assert_eq!( + SubtensorModule::get_stake_for_hotkey_on_subnet(&child, netuid), + 1000 + ); + }); +} + +// 50: Test stake retrieval for multiple parents and a single child on a subnet +/// This test verifies the functionality of retrieving stake for multiple parents and a single child on a subnet: +/// - Sets up a network with two parents and one child neuron +/// - Increases stake for both parents +/// - Sets the child as a 50% stake recipient for both parents +/// - Checks if the retrieved stake for parents and child is correct +/// SKIP_WASM_BUILD=1 RUST_LOG=debug cargo test --test children -- test_get_stake_for_hotkey_on_subnet_multiple_parents_single_child --exact --nocapture +#[test] +fn test_get_stake_for_hotkey_on_subnet_multiple_parents_single_child() { + new_test_ext(1).execute_with(|| { + let netuid: u16 = 1; + let parent1 = U256::from(1); + let parent2 = U256::from(2); + let child = U256::from(3); + let coldkey = U256::from(4); + + add_network(netuid, 0, 0); + register_ok_neuron(netuid, parent1, coldkey, 0); + register_ok_neuron(netuid, parent2, coldkey, 0); + register_ok_neuron(netuid, child, coldkey, 0); + + SubtensorModule::increase_stake_on_coldkey_hotkey_account(&coldkey, &parent1, 1000); + SubtensorModule::increase_stake_on_coldkey_hotkey_account(&coldkey, &parent2, 2000); + + assert_ok!(SubtensorModule::do_set_children( + RuntimeOrigin::signed(coldkey), + parent1, + netuid, + vec![(u64::MAX / 2, child)] + )); + assert_ok!(SubtensorModule::do_set_children( + RuntimeOrigin::signed(coldkey), + parent2, + netuid, + vec![(u64::MAX / 2, child)] + )); + + assert_eq!( + SubtensorModule::get_stake_for_hotkey_on_subnet(&parent1, netuid), + 501 + ); + assert_eq!( + SubtensorModule::get_stake_for_hotkey_on_subnet(&parent2, netuid), + 1001 + ); + assert_eq!( + SubtensorModule::get_stake_for_hotkey_on_subnet(&child, netuid), + 1498 + ); + }); +} + +// 51: Test stake retrieval for a single parent with multiple children on a subnet +/// This test verifies the functionality of retrieving stake for a single parent with multiple children on a subnet: +/// - Sets up a network with one parent and two child neurons +/// - Increases stake for the parent +/// - Sets both children as 1/3 stake recipients of the parent +/// - Checks if the retrieved stake for parent and children is correct and preserves total stake +/// SKIP_WASM_BUILD=1 RUST_LOG=debug cargo test --test children -- test_get_stake_for_hotkey_on_subnet_single_parent_multiple_children --exact --nocapture +#[test] +fn test_get_stake_for_hotkey_on_subnet_single_parent_multiple_children() { + new_test_ext(1).execute_with(|| { + let netuid: u16 = 1; + let parent = U256::from(1); + let child1 = U256::from(2); + let child2 = U256::from(3); + let coldkey = U256::from(4); + + add_network(netuid, 0, 0); + register_ok_neuron(netuid, parent, coldkey, 0); + register_ok_neuron(netuid, child1, coldkey, 0); + register_ok_neuron(netuid, child2, coldkey, 0); + + let total_stake = 3000; + SubtensorModule::increase_stake_on_coldkey_hotkey_account(&coldkey, &parent, total_stake); + + assert_ok!(SubtensorModule::do_set_children( + RuntimeOrigin::signed(coldkey), + parent, + netuid, + vec![(u64::MAX / 3, child1), (u64::MAX / 3, child2)] + )); + + let parent_stake = SubtensorModule::get_stake_for_hotkey_on_subnet(&parent, netuid); + let child1_stake = SubtensorModule::get_stake_for_hotkey_on_subnet(&child1, netuid); + let child2_stake = SubtensorModule::get_stake_for_hotkey_on_subnet(&child2, netuid); + + // Check that the total stake is preserved + assert_eq!(parent_stake + child1_stake + child2_stake, total_stake); + + // Check that the parent stake is slightly higher due to rounding + assert_eq!(parent_stake, 1002); + + // Check that each child gets an equal share of the remaining stake + assert_eq!(child1_stake, 999); + assert_eq!(child2_stake, 999); + + // Log the actual stake values + log::info!("Parent stake: {}", parent_stake); + log::info!("Child1 stake: {}", child1_stake); + log::info!("Child2 stake: {}", child2_stake); + }); +} + +// 52: Test stake retrieval for edge cases on a subnet +/// This test verifies the functionality of retrieving stake for edge cases on a subnet: +/// - Sets up a network with one parent and two child neurons +/// - Increases stake to the network maximum +/// - Sets children with 0% and 100% stake allocation +/// - Checks if the retrieved stake for parent and children is correct and preserves total stake +/// SKIP_WASM_BUILD=1 RUST_LOG=debug cargo test --test children -- test_get_stake_for_hotkey_on_subnet_edge_cases --exact --nocapture +#[test] +fn test_get_stake_for_hotkey_on_subnet_edge_cases() { + new_test_ext(1).execute_with(|| { + let netuid: u16 = 1; + let parent = U256::from(1); + let child1 = U256::from(2); + let child2 = U256::from(3); + let coldkey = U256::from(4); + + add_network(netuid, 0, 0); + register_ok_neuron(netuid, parent, coldkey, 0); + register_ok_neuron(netuid, child1, coldkey, 0); + register_ok_neuron(netuid, child2, coldkey, 0); + + // Set network max stake + let network_max_stake: u64 = 500_000_000_000_000; // 500_000 TAO + SubtensorModule::set_network_max_stake(netuid, network_max_stake); + + // Increase stake to the network max + SubtensorModule::increase_stake_on_coldkey_hotkey_account( + &coldkey, + &parent, + network_max_stake, + ); + + // Test with 0% and 100% stake allocation + assert_ok!(SubtensorModule::do_set_children( + RuntimeOrigin::signed(coldkey), + parent, + netuid, + vec![(0, child1), (u64::MAX, child2)] + )); + + let parent_stake = SubtensorModule::get_stake_for_hotkey_on_subnet(&parent, netuid); + let child1_stake = SubtensorModule::get_stake_for_hotkey_on_subnet(&child1, netuid); + let child2_stake = SubtensorModule::get_stake_for_hotkey_on_subnet(&child2, netuid); + + log::info!("Parent stake: {}", parent_stake); + log::info!("Child1 stake: {}", child1_stake); + log::info!("Child2 stake: {}", child2_stake); + + assert_eq!(parent_stake, 0, "Parent should have 0 stake"); + assert_eq!(child1_stake, 0, "Child1 should have 0 stake"); + assert_eq!( + child2_stake, network_max_stake, + "Child2 should have all the stake" + ); + + // Check that the total stake is preserved and equal to the network max stake + assert_eq!( + parent_stake + child1_stake + child2_stake, + network_max_stake, + "Total stake should equal the network max stake" + ); + }); +} + +// 53: Test stake distribution in a complex hierarchy of parent-child relationships +// This test verifies the correct distribution of stake in a multi-level parent-child hierarchy: +// - Sets up a network with four neurons: parent, child1, child2, and grandchild +// - Establishes parent-child relationships between parent and its children, and child1 and grandchild +// - Adds initial stake to the parent +// - Checks stake distribution after setting up the first level of relationships +// - Checks stake distribution after setting up the second level of relationships +// - Verifies correct stake calculations, parent-child relationships, and preservation of total stake +// SKIP_WASM_BUILD=1 RUST_LOG=debug cargo test --test children -- test_get_stake_for_hotkey_on_subnet_complex_hierarchy --exact --nocapture + +#[test] +fn test_get_stake_for_hotkey_on_subnet_complex_hierarchy() { + new_test_ext(1).execute_with(|| { + let netuid: u16 = 1; + let parent = U256::from(1); + let child1 = U256::from(2); + let child2 = U256::from(3); + let grandchild = U256::from(4); + let coldkey_parent = U256::from(5); + let coldkey_child1 = U256::from(6); + let coldkey_child2 = U256::from(7); + let coldkey_grandchild = U256::from(8); + + add_network(netuid, 0, 0); + SubtensorModule::set_max_registrations_per_block(netuid, 1000); + SubtensorModule::set_target_registrations_per_interval(netuid, 1000); + register_ok_neuron(netuid, parent, coldkey_parent, 0); + register_ok_neuron(netuid, child1, coldkey_child1, 0); + register_ok_neuron(netuid, child2, coldkey_child2, 0); + register_ok_neuron(netuid, grandchild, coldkey_grandchild, 0); + + let total_stake = 1000; + SubtensorModule::increase_stake_on_coldkey_hotkey_account( + &coldkey_parent, + &parent, + total_stake, + ); + + log::info!("Initial stakes:"); + log::info!( + "Parent stake: {}", + SubtensorModule::get_stake_for_hotkey_on_subnet(&parent, netuid) + ); + log::info!( + "Child1 stake: {}", + SubtensorModule::get_stake_for_hotkey_on_subnet(&child1, netuid) + ); + log::info!( + "Child2 stake: {}", + SubtensorModule::get_stake_for_hotkey_on_subnet(&child2, netuid) + ); + log::info!( + "Grandchild stake: {}", + SubtensorModule::get_stake_for_hotkey_on_subnet(&grandchild, netuid) + ); + + // Step 1: Set children for parent + assert_ok!(SubtensorModule::do_set_children( + RuntimeOrigin::signed(coldkey_parent), + parent, + netuid, + vec![(u64::MAX / 2, child1), (u64::MAX / 2, child2)] + )); + + log::info!("After setting parent's children:"); + log::info!( + "Parent's children: {:?}", + SubtensorModule::get_children(&parent, netuid) + ); + log::info!( + "Child1's parents: {:?}", + SubtensorModule::get_parents(&child1, netuid) + ); + log::info!( + "Child2's parents: {:?}", + SubtensorModule::get_parents(&child2, netuid) + ); + + let parent_stake_1 = SubtensorModule::get_stake_for_hotkey_on_subnet(&parent, netuid); + let child1_stake_1 = SubtensorModule::get_stake_for_hotkey_on_subnet(&child1, netuid); + let child2_stake_1 = SubtensorModule::get_stake_for_hotkey_on_subnet(&child2, netuid); + + log::info!("Parent stake: {}", parent_stake_1); + log::info!("Child1 stake: {}", child1_stake_1); + log::info!("Child2 stake: {}", child2_stake_1); + + assert_eq!( + parent_stake_1, 2, + "Parent should have 2 stake due to rounding" + ); + assert_eq!(child1_stake_1, 499, "Child1 should have 499 stake"); + assert_eq!(child2_stake_1, 499, "Child2 should have 499 stake"); + + // Step 2: Set children for child1 + assert_ok!(SubtensorModule::do_set_children( + RuntimeOrigin::signed(coldkey_child1), + child1, + netuid, + vec![(u64::MAX, grandchild)] + )); + + log::info!("After setting child1's children:"); + log::info!( + "Child1's children: {:?}", + SubtensorModule::get_children(&child1, netuid) + ); + log::info!( + "Grandchild's parents: {:?}", + SubtensorModule::get_parents(&grandchild, netuid) + ); + + let parent_stake_2 = SubtensorModule::get_stake_for_hotkey_on_subnet(&parent, netuid); + let child1_stake_2 = SubtensorModule::get_stake_for_hotkey_on_subnet(&child1, netuid); + let child2_stake_2 = SubtensorModule::get_stake_for_hotkey_on_subnet(&child2, netuid); + let grandchild_stake = SubtensorModule::get_stake_for_hotkey_on_subnet(&grandchild, netuid); + + log::info!("Parent stake: {}", parent_stake_2); + log::info!("Child1 stake: {}", child1_stake_2); + log::info!("Child2 stake: {}", child2_stake_2); + log::info!("Grandchild stake: {}", grandchild_stake); + + assert_eq!(parent_stake_2, 2, "Parent stake should remain 2"); + assert_eq!( + child1_stake_2, 499, + "Child1 stake should be be the same , as it doesnt have owned stake" + ); + assert_eq!(child2_stake_2, 499, "Child2 should still have 499 stake"); + assert_eq!( + grandchild_stake, 0, + "Grandchild should have 0 , as child1 doesnt have any owned stake" + ); + + // Check that the total stake is preserved + assert_eq!( + parent_stake_2 + child1_stake_2 + child2_stake_2 + grandchild_stake, + total_stake, + "Total stake should equal the initial stake" + ); + + // Additional checks + log::info!("Final parent-child relationships:"); + log::info!( + "Parent's children: {:?}", + SubtensorModule::get_children(&parent, netuid) + ); + log::info!( + "Child1's parents: {:?}", + SubtensorModule::get_parents(&child1, netuid) + ); + log::info!( + "Child2's parents: {:?}", + SubtensorModule::get_parents(&child2, netuid) + ); + log::info!( + "Child1's children: {:?}", + SubtensorModule::get_children(&child1, netuid) + ); + log::info!( + "Grandchild's parents: {:?}", + SubtensorModule::get_parents(&grandchild, netuid) + ); + + // Check if the parent-child relationships are correct + assert_eq!( + SubtensorModule::get_children(&parent, netuid), + vec![(u64::MAX / 2, child1), (u64::MAX / 2, child2)], + "Parent should have both children" + ); + assert_eq!( + SubtensorModule::get_parents(&child1, netuid), + vec![(u64::MAX / 2, parent)], + "Child1 should have parent as its parent" + ); + assert_eq!( + SubtensorModule::get_parents(&child2, netuid), + vec![(u64::MAX / 2, parent)], + "Child2 should have parent as its parent" + ); + assert_eq!( + SubtensorModule::get_children(&child1, netuid), + vec![(u64::MAX, grandchild)], + "Child1 should have grandchild as its child" + ); + assert_eq!( + SubtensorModule::get_parents(&grandchild, netuid), + vec![(u64::MAX, child1)], + "Grandchild should have child1 as its parent" + ); + }); +} + +// 54: Test stake distribution across multiple networks +// This test verifies the correct distribution of stake for a single neuron across multiple networks: +// - Sets up two networks with a single neuron registered on both +// - Adds initial stake to the neuron +// - Checks that the stake is correctly reflected on both networks +// - Verifies that changes in stake are consistently applied across all networks +// SKIP_WASM_BUILD=1 RUST_LOG=debug cargo test --test children -- test_get_stake_for_hotkey_on_subnet_multiple_networks --exact --nocapture + +#[test] +fn test_get_stake_for_hotkey_on_subnet_multiple_networks() { + new_test_ext(1).execute_with(|| { + let netuid1: u16 = 1; + let netuid2: u16 = 2; + let hotkey = U256::from(1); + let coldkey = U256::from(2); + + add_network(netuid1, 0, 0); + add_network(netuid2, 0, 0); + register_ok_neuron(netuid1, hotkey, coldkey, 0); + register_ok_neuron(netuid2, hotkey, coldkey, 0); + + SubtensorModule::increase_stake_on_coldkey_hotkey_account(&coldkey, &hotkey, 1000); + + assert_eq!( + SubtensorModule::get_stake_for_hotkey_on_subnet(&hotkey, netuid1), + 1000 + ); + assert_eq!( + SubtensorModule::get_stake_for_hotkey_on_subnet(&hotkey, netuid2), + 1000 + ); + }); +} + +/// 55: Test rank, trust, and incentive calculation with parent-child relationships +/// +/// This test verifies the correct calculation and distribution of rank, trust, incentive, and dividends +/// in a network with parent-child relationships: +/// - Sets up a network with validators (including a parent-child pair) and miners +/// - Establishes initial stakes and weights for all validators +/// - Runs a first epoch to establish baseline metrics +/// - Sets up a parent-child relationship +/// - Runs a second epoch to observe changes in metrics +/// - Verifies that the child's metrics improve relative to its initial state and other validators +/// +/// # Test Steps: +/// 1. Initialize test environment with validators (including parent and child) and miners +/// 2. Set up network parameters and register all neurons +/// 3. Set initial stakes for validators +/// 4. Set initial weights for all validators +/// 5. Run first epoch and process emissions +/// 6. Record initial metrics for the child +/// 7. Establish parent-child relationship +/// 8. Run second epoch and process emissions +/// 9. Record final metrics for the child +/// 10. Compare child's initial and final metrics +/// 11. Compare child's final metrics with other validators +/// +/// # Expected Results: +/// - Child's rank should improve (decrease) +/// - Child's trust should increase or remain the same +/// - Child's dividends should increase +/// - Child's final metrics should be better than or equal to other validators' +/// +/// SKIP_WASM_BUILD=1 RUST_LOG=debug cargo test --test children -- test_rank_trust_incentive_calculation_with_parent_child --exact --nocapture +#[test] +fn test_rank_trust_incentive_calculation_with_parent_child() { + new_test_ext(1).execute_with(|| { + // Initialize test environment + let netuid: u16 = 1; + let parent_hotkey: U256 = U256::from(1); + let parent_coldkey: U256 = U256::from(101); + let child_hotkey: U256 = U256::from(2); + let child_coldkey: U256 = U256::from(102); + let other_validators: Vec<(U256, U256)> = (3..6) + .map(|i| (U256::from(i), U256::from(100 + i))) + .collect(); + let miners: Vec<(U256, U256)> = (6..16) + .map(|i| (U256::from(i), U256::from(100 + i))) + .collect(); // 10 miners + + // Setup network and set registration parameters + add_network(netuid, 1, 0); + SubtensorModule::set_max_registrations_per_block(netuid, 1000); + SubtensorModule::set_target_registrations_per_interval(netuid, 1000); + SubtensorModule::set_weights_set_rate_limit(netuid, 0); + SubtensorModule::set_hotkey_emission_tempo(10); + + // Register neurons (validators and miners) + register_ok_neuron(netuid, parent_hotkey, parent_coldkey, 0); + register_ok_neuron(netuid, child_hotkey, child_coldkey, 0); + for (hotkey, coldkey) in &other_validators { + register_ok_neuron(netuid, *hotkey, *coldkey, 0); + } + for (hotkey, coldkey) in &miners { + register_ok_neuron(netuid, *hotkey, *coldkey, 0); + } + + step_block(2); + + // Set initial stakes for validators only + let initial_stake: u64 = 1_000_000_000; // 1000 TAO + SubtensorModule::add_balance_to_coldkey_account(&parent_coldkey, initial_stake); + SubtensorModule::increase_stake_on_coldkey_hotkey_account( + &parent_coldkey, + &parent_hotkey, + initial_stake, + ); + SubtensorModule::add_balance_to_coldkey_account(&child_coldkey, initial_stake); + SubtensorModule::increase_stake_on_coldkey_hotkey_account( + &child_coldkey, + &child_hotkey, + initial_stake, + ); + for (hotkey, coldkey) in &other_validators { + SubtensorModule::add_balance_to_coldkey_account(coldkey, initial_stake); + SubtensorModule::increase_stake_on_coldkey_hotkey_account( + coldkey, + hotkey, + initial_stake, + ); + } + + step_block(2); + + // Set initial weights for all validators + let all_uids: Vec = (0..15).collect(); // 0-4 are validators, 5-14 are miners + let validator_weights: Vec = vec![u16::MAX / 5; 5] // Equal weights for validators + .into_iter() + .chain(vec![u16::MAX / 10; 10]) // Equal weights for miners + .collect(); + + for hotkey in std::iter::once(&parent_hotkey) + .chain(other_validators.iter().map(|(h, _)| h)) + .chain(std::iter::once(&child_hotkey)) + { + assert_ok!(SubtensorModule::set_weights( + RuntimeOrigin::signed(*hotkey), + netuid, + all_uids.clone(), + validator_weights.clone(), + 0 + )); + } + + step_block(10); + + // Run first epoch + let rao_emission: u64 = 1_000_000_000; + let initial_emission = SubtensorModule::epoch(netuid, rao_emission); + + // Process initial emission + for (hotkey, mining_emission, validator_emission) in initial_emission { + SubtensorModule::accumulate_hotkey_emission( + &hotkey, + netuid, + validator_emission, + mining_emission, + ); + } + + step_block(11); + + // Get initial rank, trust, incentive, and dividends for the child + let initial_child_rank: u16 = SubtensorModule::get_rank_for_uid(netuid, 1); + let initial_child_trust: u16 = SubtensorModule::get_trust_for_uid(netuid, 1); + let initial_child_incentive: u16 = SubtensorModule::get_incentive_for_uid(netuid, 1); + let initial_child_dividends: u16 = SubtensorModule::get_dividends_for_uid(netuid, 1); + + log::debug!("Initial child rank: {:?}", initial_child_rank); + log::debug!("Initial child trust: {:?}", initial_child_trust); + log::debug!("Initial child incentive: {:?}", initial_child_incentive); + log::debug!("Initial child dividends: {:?}", initial_child_dividends); + + // Parent sets the child with 100% of its weight + assert_ok!(SubtensorModule::do_set_children( + RuntimeOrigin::signed(parent_coldkey), + parent_hotkey, + netuid, + vec![(u64::MAX, child_hotkey)] + )); + + // Child now sets weights as a validator + assert_ok!(SubtensorModule::set_weights( + RuntimeOrigin::signed(child_hotkey), + netuid, + all_uids.clone(), + validator_weights.clone(), + 1 + )); + + step_block(10); + + // Run second epoch + let final_emission = SubtensorModule::epoch(netuid, rao_emission); + + // Process final emission + for (hotkey, mining_emission, validator_emission) in final_emission { + SubtensorModule::accumulate_hotkey_emission( + &hotkey, + netuid, + validator_emission, + mining_emission, + ); + } + + step_block(11); + + // Get final rank, trust, incentive, and dividends for the child + let final_child_rank: u16 = SubtensorModule::get_rank_for_uid(netuid, 1); + let final_child_trust: u16 = SubtensorModule::get_trust_for_uid(netuid, 1); + let final_child_incentive: u16 = SubtensorModule::get_incentive_for_uid(netuid, 1); + let final_child_dividends: u16 = SubtensorModule::get_dividends_for_uid(netuid, 1); + + log::debug!("Final child rank: {:?}", final_child_rank); + log::debug!("Final child trust: {:?}", final_child_trust); + log::debug!("Final child incentive: {:?}", final_child_incentive); + log::debug!("Final child dividends: {:?}", final_child_dividends); + + // Print ranks for all validators + for i in 0..5 { + log::debug!( + "Validator {} rank: {:?}", + i, + SubtensorModule::get_rank_for_uid(netuid, i) + ); + } + + // Assert that rank has improved (decreased) for the child + assert!( + final_child_rank < initial_child_rank, + "Child rank should have improved (decreased). Initial: {}, Final: {}", + initial_child_rank, + final_child_rank + ); + + // Assert that trust has increased or remained the same for the child + assert!( + final_child_trust >= initial_child_trust, + "Child trust should have increased or remained the same. Initial: {}, Final: {}", + initial_child_trust, + final_child_trust + ); + + + // Assert that dividends have increased for the child + assert!( + final_child_dividends > initial_child_dividends, + "Child dividends should have increased. Initial: {}, Final: {}", + initial_child_dividends, + final_child_dividends + ); + + // Compare child's final values with other validators + for i in 2..5 { + let other_rank: u16 = SubtensorModule::get_rank_for_uid(netuid, i); + let other_trust: u16 = SubtensorModule::get_trust_for_uid(netuid, i); + let other_incentive: u16 = SubtensorModule::get_incentive_for_uid(netuid, i); + let other_dividends: u16 = SubtensorModule::get_dividends_for_uid(netuid, i); + + log::debug!( + "Validator {} - Rank: {}, Trust: {}, Incentive: {}, Dividends: {}", + i, other_rank, other_trust, other_incentive, other_dividends + ); + + assert!( + final_child_rank <= other_rank, + "Child rank should be better than or equal to other validators. Child: {}, Other: {}", + final_child_rank, + other_rank + ); + + assert!( + final_child_trust >= other_trust, + "Child trust should be greater than or equal to other validators. Child: {}, Other: {}", + final_child_trust, + other_trust + ); + + assert!( + final_child_dividends >= other_dividends, + "Child dividends should be greater than or equal to other validators. Child: {}, Other: {}", + final_child_dividends, + other_dividends + ); + } + + }); +} diff --git a/pallets/subtensor/tests/coinbase.rs b/pallets/subtensor/tests/coinbase.rs index d6e48bbcc..a6c1acde1 100644 --- a/pallets/subtensor/tests/coinbase.rs +++ b/pallets/subtensor/tests/coinbase.rs @@ -6,6 +6,7 @@ use sp_core::U256; // Test the ability to hash all sorts of hotkeys. #[test] + fn test_hotkey_hashing() { new_test_ext(1).execute_with(|| { for i in 0..10000 { @@ -17,6 +18,7 @@ fn test_hotkey_hashing() { // Test drain tempo on hotkeys. // SKIP_WASM_BUILD=1 RUST_LOG=debug cargo test --test coinbase test_hotkey_drain_time -- --nocapture #[test] + fn test_hotkey_drain_time() { new_test_ext(1).execute_with(|| { // Block 0 @@ -44,6 +46,7 @@ fn test_hotkey_drain_time() { // To run this test specifically, use the following command: // SKIP_WASM_BUILD=1 RUST_LOG=debug cargo test --test coinbase test_coinbase_basic -- --nocapture #[test] + fn test_coinbase_basic() { new_test_ext(1).execute_with(|| { // Define network ID @@ -135,6 +138,7 @@ fn test_coinbase_basic() { // Test getting and setting hotkey emission tempo // SKIP_WASM_BUILD=1 RUST_LOG=debug cargo test --test coinbase test_set_and_get_hotkey_emission_tempo -- --nocapture #[test] + fn test_set_and_get_hotkey_emission_tempo() { new_test_ext(1).execute_with(|| { // Get the default hotkey emission tempo diff --git a/pallets/subtensor/tests/epoch.rs b/pallets/subtensor/tests/epoch.rs index b639a4ac4..9c4bf87cc 100644 --- a/pallets/subtensor/tests/epoch.rs +++ b/pallets/subtensor/tests/epoch.rs @@ -2703,6 +2703,53 @@ fn test_get_set_alpha() { }); } +#[test] +fn test_blocks_since_last_step() { + new_test_ext(1).execute_with(|| { + System::set_block_number(0); + + let netuid: u16 = 1; + let tempo: u16 = 7200; + add_network(netuid, tempo, 0); + + let original_blocks: u64 = SubtensorModule::get_blocks_since_last_step(netuid); + + step_block(5); + + let new_blocks: u64 = SubtensorModule::get_blocks_since_last_step(netuid); + + assert!(new_blocks > original_blocks); + assert_eq!(new_blocks, 5); + + let blocks_to_step: u16 = SubtensorModule::blocks_until_next_epoch( + netuid, + tempo, + SubtensorModule::get_current_block_as_u64(), + ) as u16 + + 10; + step_block(blocks_to_step); + + let post_blocks: u64 = SubtensorModule::get_blocks_since_last_step(netuid); + + assert_eq!(post_blocks, 10); + + let blocks_to_step: u16 = SubtensorModule::blocks_until_next_epoch( + netuid, + tempo, + SubtensorModule::get_current_block_as_u64(), + ) as u16 + + 20; + step_block(blocks_to_step); + + let new_post_blocks: u64 = SubtensorModule::get_blocks_since_last_step(netuid); + + assert_eq!(new_post_blocks, 20); + + step_block(7); + + assert_eq!(SubtensorModule::get_blocks_since_last_step(netuid), 27); + }); +} // // Map the retention graph for consensus guarantees with an single epoch on a graph with 512 nodes, of which the first 64 are validators, the graph is split into a major and minor set, each setting specific weight on itself and the complement on the other. // // // // ```import torch diff --git a/pallets/subtensor/tests/migration.rs b/pallets/subtensor/tests/migration.rs index bd77c7be0..2a1388237 100644 --- a/pallets/subtensor/tests/migration.rs +++ b/pallets/subtensor/tests/migration.rs @@ -6,6 +6,29 @@ use mock::*; use pallet_subtensor::*; use sp_core::U256; +#[test] +fn test_initialise_ti() { + use frame_support::traits::OnRuntimeUpgrade; + + new_test_ext(1).execute_with(|| { + pallet_subtensor::SubnetLocked::::insert(1, 100); + pallet_subtensor::SubnetLocked::::insert(2, 5); + pallet_balances::TotalIssuance::::put(1000); + pallet_subtensor::TotalStake::::put(25); + + // Ensure values are NOT initialized prior to running migration + assert!(pallet_subtensor::TotalIssuance::::get() == 0); + + pallet_subtensor::migrations::migrate_init_total_issuance::initialise_total_issuance::Migration::::on_runtime_upgrade(); + + // Ensure values were initialized correctly + assert!( + pallet_subtensor::TotalIssuance::::get() + == 105u64.saturating_add(1000).saturating_add(25) + ); + }); +} + #[test] fn test_migration_fix_total_stake_maps() { new_test_ext(1).execute_with(|| { diff --git a/pallets/subtensor/tests/mock.rs b/pallets/subtensor/tests/mock.rs index d8d677006..aa93c3531 100644 --- a/pallets/subtensor/tests/mock.rs +++ b/pallets/subtensor/tests/mock.rs @@ -2,11 +2,10 @@ use frame_support::derive_impl; use frame_support::dispatch::DispatchResultWithPostInfo; use frame_support::weights::constants::RocksDbWeight; -// use frame_support::weights::constants::WEIGHT_PER_SECOND; use frame_support::weights::Weight; use frame_support::{ assert_ok, parameter_types, - traits::{Everything, Hooks}, + traits::{Everything, Hooks, PrivilegeCmp}, }; use frame_system as system; use frame_system::{limits, EnsureNever, EnsureRoot, RawOrigin}; @@ -17,6 +16,7 @@ use sp_runtime::{ traits::{BlakeTwo256, IdentityLookup}, BuildStorage, }; +use sp_std::cmp::Ordering; type Block = frame_system::mocking::MockBlock; @@ -32,6 +32,8 @@ frame_support::construct_runtime!( SenateMembers: pallet_membership::::{Pallet, Call, Storage, Event, Config}, SubtensorModule: pallet_subtensor::{Pallet, Call, Storage, Event}, Utility: pallet_utility::{Pallet, Call, Storage, Event}, + Scheduler: pallet_scheduler::{Pallet, Call, Storage, Event}, + Preimage: pallet_preimage::{Pallet, Call, Storage, Event}, } ); @@ -78,7 +80,6 @@ impl pallet_balances::Config for Test { type WeightInfo = (); type MaxReserves = (); type ReserveIdentifier = (); - type RuntimeHoldReason = (); type FreezeIdentifier = (); type MaxFreezes = (); @@ -131,12 +132,16 @@ parameter_types! { pub const InitialBondsMovingAverage: u64 = 900_000; 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 =5_898; // 9%; + pub const InitialDefaultDelegateTake: u16 = 11_796; // 18%, same as in production + pub const InitialMinDelegateTake: u16 = 5_898; // 9%; + pub const InitialDefaultChildKeyTake: u16 = 0 ;// 0 % + pub const InitialMinChildKeyTake: u16 = 0; // 0 %; + pub const InitialMaxChildKeyTake: u16 = 11_796; // 18 %; pub const InitialWeightsVersionKey: u16 = 0; pub const InitialServingRateLimit: u64 = 0; // No limit. pub const InitialTxRateLimit: u64 = 0; // Disable rate limit for testing pub const InitialTxDelegateTakeRateLimit: u64 = 1; // 1 block take rate limit for testing + pub const InitialTxChildKeyTakeRateLimit: u64 = 1; // 1 block take rate limit for testing pub const InitialBurn: u64 = 0; pub const InitialMinBurn: u64 = 0; pub const InitialMaxBurn: u64 = 1_000_000_000; @@ -169,8 +174,9 @@ parameter_types! { pub const InitialAlphaLow: u16 = 45875; // Represents 0.7 as per the production default pub const InitialLiquidAlphaOn: bool = false; // Default value for LiquidAlphaOn pub const InitialHotkeyEmissionTempo: u64 = 0; // Defaults to draining every block. - pub const InitialNetworkMaxStake: u64 = 500_000_000_000_000; // 500,000 TAO - + pub const InitialNetworkMaxStake: u64 = u64::MAX; // Maximum possible value for u64 + pub const InitialColdkeySwapScheduleDuration: u64 = 5 * 24 * 60 * 60 / 12; // Default as 5 days + pub const InitialDissolveNetworkScheduleDuration: u64 = 5 * 24 * 60 * 60 / 12; // Default as 5 days } // Configure collective pallet for council @@ -334,13 +340,14 @@ impl pallet_membership::Config for Test { impl pallet_subtensor::Config for Test { type RuntimeEvent = RuntimeEvent; + type RuntimeCall = RuntimeCall; type Currency = Balances; type InitialIssuance = InitialIssuance; type SudoRuntimeCall = TestRuntimeCall; type CouncilOrigin = frame_system::EnsureSigned; type SenateMembers = ManageSenateMembers; type TriumvirateInterface = TriumvirateVotes; - + type Scheduler = Scheduler; type InitialMinAllowedWeights = InitialMinAllowedWeights; type InitialEmissionValue = InitialEmissionValue; type InitialMaxWeightsLimit = InitialMaxWeightsLimit; @@ -360,8 +367,12 @@ impl pallet_subtensor::Config for Test { type InitialPruningScore = InitialPruningScore; type InitialBondsMovingAverage = InitialBondsMovingAverage; type InitialMaxAllowedValidators = InitialMaxAllowedValidators; - type InitialDefaultTake = InitialDefaultTake; - type InitialMinTake = InitialMinTake; + type InitialDefaultDelegateTake = InitialDefaultDelegateTake; + type InitialMinDelegateTake = InitialMinDelegateTake; + type InitialDefaultChildKeyTake = InitialDefaultChildKeyTake; + type InitialMinChildKeyTake = InitialMinChildKeyTake; + type InitialMaxChildKeyTake = InitialMaxChildKeyTake; + type InitialTxChildKeyTakeRateLimit = InitialTxChildKeyTakeRateLimit; type InitialWeightsVersionKey = InitialWeightsVersionKey; type InitialMaxDifficulty = InitialMaxDifficulty; type InitialMinDifficulty = InitialMinDifficulty; @@ -387,6 +398,37 @@ impl pallet_subtensor::Config for Test { type LiquidAlphaOn = InitialLiquidAlphaOn; type InitialHotkeyEmissionTempo = InitialHotkeyEmissionTempo; type InitialNetworkMaxStake = InitialNetworkMaxStake; + type Preimages = Preimage; + type InitialColdkeySwapScheduleDuration = InitialColdkeySwapScheduleDuration; + type InitialDissolveNetworkScheduleDuration = InitialDissolveNetworkScheduleDuration; +} + +pub struct OriginPrivilegeCmp; + +impl PrivilegeCmp for OriginPrivilegeCmp { + fn cmp_privilege(_left: &OriginCaller, _right: &OriginCaller) -> Option { + Some(Ordering::Less) + } +} + +parameter_types! { + pub MaximumSchedulerWeight: Weight = Perbill::from_percent(80) * + BlockWeights::get().max_block; + pub const MaxScheduledPerBlock: u32 = 50; + pub const NoPreimagePostponement: Option = Some(10); +} + +impl pallet_scheduler::Config for Test { + type RuntimeOrigin = RuntimeOrigin; + type RuntimeEvent = RuntimeEvent; + type PalletsOrigin = OriginCaller; + type RuntimeCall = RuntimeCall; + type MaximumWeight = MaximumSchedulerWeight; + type ScheduleOrigin = EnsureRoot; + type MaxScheduledPerBlock = MaxScheduledPerBlock; + type WeightInfo = pallet_scheduler::weights::SubstrateWeight; + type OriginPrivilegeCmp = OriginPrivilegeCmp; + type Preimages = Preimage; } impl pallet_utility::Config for Test { @@ -396,6 +438,20 @@ impl pallet_utility::Config for Test { type WeightInfo = pallet_utility::weights::SubstrateWeight; } +parameter_types! { + pub const PreimageMaxSize: u32 = 4096 * 1024; + pub const PreimageBaseDeposit: Balance = 1; + pub const PreimageByteDeposit: Balance = 1; +} + +impl pallet_preimage::Config for Test { + type WeightInfo = pallet_preimage::weights::SubstrateWeight; + type RuntimeEvent = RuntimeEvent; + type Currency = Balances; + type ManagerOrigin = EnsureRoot; + type Consideration = (); +} + #[allow(dead_code)] // Build genesis storage according to the mock runtime. pub fn new_test_ext(block_number: BlockNumber) -> sp_io::TestExternalities { @@ -430,22 +486,30 @@ pub fn test_ext_with_balances(balances: Vec<(U256, u128)>) -> sp_io::TestExterna #[allow(dead_code)] pub(crate) fn step_block(n: u16) { for _ in 0..n { + Scheduler::on_finalize(System::block_number()); SubtensorModule::on_finalize(System::block_number()); System::on_finalize(System::block_number()); System::set_block_number(System::block_number() + 1); System::on_initialize(System::block_number()); SubtensorModule::on_initialize(System::block_number()); + Scheduler::on_initialize(System::block_number()); } } #[allow(dead_code)] pub(crate) fn run_to_block(n: u64) { while System::block_number() < n { + Scheduler::on_finalize(System::block_number()); SubtensorModule::on_finalize(System::block_number()); System::on_finalize(System::block_number()); System::set_block_number(System::block_number() + 1); System::on_initialize(System::block_number()); + System::events().iter().for_each(|event| { + log::info!("Event: {:?}", event.event); + }); + System::reset_events(); SubtensorModule::on_initialize(System::block_number()); + Scheduler::on_initialize(System::block_number()); } } @@ -501,3 +565,21 @@ pub fn add_network(netuid: u16, tempo: u16, _modality: u16) { SubtensorModule::set_network_registration_allowed(netuid, true); SubtensorModule::set_network_pow_registration_allowed(netuid, true); } + +// Helper function to set up a neuron with stake +#[allow(dead_code)] +pub fn setup_neuron_with_stake(netuid: u16, hotkey: U256, coldkey: U256, stake: u64) { + register_ok_neuron(netuid, hotkey, coldkey, stake); + SubtensorModule::increase_stake_on_coldkey_hotkey_account(&coldkey, &hotkey, stake); +} + +// Helper function to check if a value is within tolerance of an expected value +#[allow(dead_code)] +pub fn is_within_tolerance(actual: u64, expected: u64, tolerance: u64) -> bool { + let difference = if actual > expected { + actual - expected + } else { + expected - actual + }; + difference <= tolerance +} diff --git a/pallets/subtensor/tests/networks.rs b/pallets/subtensor/tests/networks.rs index 93e563683..b41ff985d 100644 --- a/pallets/subtensor/tests/networks.rs +++ b/pallets/subtensor/tests/networks.rs @@ -1,420 +1,96 @@ -// DEPRECATED mod mock; -// use frame_support::{ -// assert_ok, -// dispatch::{DispatchClass, DispatchInfo, GetDispatchInfo, Pays}, -// sp_std::vec, -// }; -// use frame_system::Config; -// use frame_system::{EventRecord, Phase}; -// use mock::*; -// use pallet_subtensor::Error; -// use sp_core::{H256, U256}; - -// #[allow(dead_code)] -// fn record(event: RuntimeEvent) -> EventRecord { -// EventRecord { -// phase: Phase::Initialization, -// event, -// topics: vec![], -// } -// } - -// /*TO DO SAM: write test for LatuUpdate after it is set */ -// // --- add network tests ---- -// #[test] -// fn test_add_network_dispatch_info_ok() { -// new_test_ext().execute_with(|| { -// let netuid: u16 = 1; -// let modality = 0; -// let tempo: u16 = 13; -// let call = RuntimeCall::SubtensorModule(SubtensorCall::sudo_add_network { -// netuid, -// tempo, -// modality, -// }); -// assert_eq!( -// call.get_dispatch_info(), -// DispatchInfo { -// weight: frame_support::weights::Weight::from_parts(50000000, 0), -// class: DispatchClass::Operational, -// pays_fee: Pays::No -// } -// ); -// }); -// } - -// #[test] -// fn test_add_network() { -// new_test_ext().execute_with(|| { -// let modality = 0; -// let tempo: u16 = 13; -// add_network(10, tempo, modality); -// assert_eq!(SubtensorModule::get_number_of_subnets(), 1); -// add_network(20, tempo, modality); -// assert_eq!(SubtensorModule::get_number_of_subnets(), 2); -// }); -// } - -// #[test] -// fn test_add_network_check_tempo() { -// new_test_ext().execute_with(|| { -// let modality = 0; -// let tempo: u16 = 13; -// assert_eq!(SubtensorModule::get_tempo(1), 0); -// add_network(1, tempo, modality); -// assert_eq!(SubtensorModule::get_tempo(1), 13); -// }); -// } - -// #[test] -// fn test_clear_min_allowed_weight_for_network() { -// new_test_ext().execute_with(|| { -// let netuid: u16 = 1; -// let min_allowed_weight = 2; -// let tempo: u16 = 13; -// add_network(netuid, tempo, 0); -// register_ok_neuron(1, U256::from(55), U256::from(66), 0); -// SubtensorModule::set_min_allowed_weights(netuid, min_allowed_weight); -// assert_eq!(SubtensorModule::get_min_allowed_weights(netuid), 2); -// assert_ok!(SubtensorModule::do_remove_network( -// <::RuntimeOrigin>::root(), -// netuid -// )); -// assert_eq!(SubtensorModule::get_min_allowed_weights(netuid), 0); -// }); -// } - -// #[test] -// fn test_remove_uid_for_network() { -// new_test_ext().execute_with(|| { -// let netuid: u16 = 1; -// let tempo: u16 = 13; -// add_network(netuid, tempo, 0); -// register_ok_neuron(1, U256::from(55), U256::from(66), 0); -// let neuron_id; -// match SubtensorModule::get_uid_for_net_and_hotkey(netuid, &U256::from(55)) { -// Ok(k) => neuron_id = k, -// Err(e) => panic!("Error: {:?}", e), -// } -// assert!(SubtensorModule::get_uid_for_net_and_hotkey(netuid, &U256::from(55)).is_ok()); -// assert_eq!(neuron_id, 0); -// register_ok_neuron(1, U256::from(56), U256::from(67), 300000); -// let neuron_uid = -// SubtensorModule::get_uid_for_net_and_hotkey(netuid, &U256::from(56)).unwrap(); -// assert_eq!(neuron_uid, 1); -// assert_ok!(SubtensorModule::do_remove_network( -// <::RuntimeOrigin>::root(), -// netuid -// )); -// assert!(SubtensorModule::get_uid_for_net_and_hotkey(netuid, &U256::from(55)).is_err()); -// }); -// } - -// #[test] -// fn test_remove_difficulty_for_network() { -// new_test_ext().execute_with(|| { -// let netuid: u16 = 1; -// let difficulty: u64 = 10; -// let tempo: u16 = 13; -// add_network(netuid, tempo, 0); -// register_ok_neuron(1, U256::from(55), U256::from(66), 0); -// assert_ok!(SubtensorModule::sudo_set_difficulty( -// <::RuntimeOrigin>::root(), -// netuid, -// difficulty -// )); -// assert_eq!(SubtensorModule::get_difficulty_as_u64(netuid), difficulty); -// assert_ok!(SubtensorModule::do_remove_network( -// <::RuntimeOrigin>::root(), -// netuid -// )); -// assert_eq!(SubtensorModule::get_difficulty_as_u64(netuid), 10000); -// }); -// } - -// #[test] -// fn test_remove_network_for_all_hotkeys() { -// new_test_ext().execute_with(|| { -// let netuid: u16 = 1; -// let tempo: u16 = 13; -// add_network(netuid, tempo, 0); -// register_ok_neuron(1, U256::from(55), U256::from(66), 0); -// register_ok_neuron(1, U256::from(77), U256::from(88), 65536); -// assert_eq!(SubtensorModule::get_subnetwork_n(netuid), 2); -// assert_ok!(SubtensorModule::do_remove_network( -// <::RuntimeOrigin>::root(), -// netuid -// )); -// assert_eq!(SubtensorModule::get_subnetwork_n(netuid), 0); -// }); -// } - -// #[test] -// fn test_network_set_default_value_for_other_parameters() { -// new_test_ext().execute_with(|| { -// let netuid: u16 = 1; -// let tempo: u16 = 13; -// add_network(netuid, tempo, 0); -// assert_eq!(SubtensorModule::get_min_allowed_weights(netuid), 0); -// assert_eq!(SubtensorModule::get_emission_value(netuid), 0); -// assert_eq!(SubtensorModule::get_max_weight_limit(netuid), u16::MAX); -// assert_eq!(SubtensorModule::get_difficulty_as_u64(netuid), 10000); -// assert_eq!(SubtensorModule::get_immunity_period(netuid), 2); -// }); -// } - -// // --- Set Emission Ratios Tests -// #[test] -// fn test_network_set_emission_ratios_dispatch_info_ok() { -// new_test_ext().execute_with(|| { -// let netuids: Vec = vec![1, 2]; -// let emission: Vec = vec![100000000, 900000000]; -// let call = RuntimeCall::SubtensorModule(SubtensorCall::sudo_set_emission_values { -// netuids, -// emission, -// }); -// assert_eq!( -// call.get_dispatch_info(), -// DispatchInfo { -// weight: frame_support::weights::Weight::from_parts(28000000, 0), -// class: DispatchClass::Operational, -// pays_fee: Pays::No -// } -// ); -// }); -// } - -// #[test] -// fn test_network_set_emission_ratios_ok() { -// new_test_ext().execute_with(|| { -// let netuids: Vec = vec![1, 2]; -// let emission: Vec = vec![100000000, 900000000]; -// add_network(1, 0, 0); -// add_network(2, 0, 0); -// assert_ok!(SubtensorModule::sudo_set_emission_values( -// <::RuntimeOrigin>::root(), -// netuids, -// emission -// )); -// }); -// } - -// #[test] -// fn test_network_set_emission_ratios_fail_summation() { -// new_test_ext().execute_with(|| { -// let netuids: Vec = vec![1, 2]; -// let emission: Vec = vec![100000000, 910000000]; -// add_network(1, 0, 0); -// add_network(2, 0, 0); -// assert_eq!( -// SubtensorModule::sudo_set_emission_values( -// <::RuntimeOrigin>::root(), -// netuids, -// emission -// ), -// Err(Error::::InvalidEmissionValues.into()) -// ); -// }); -// } - -// #[test] -// fn test_network_set_emission_invalid_netuids() { -// new_test_ext().execute_with(|| { -// let netuids: Vec = vec![1, 2]; -// let emission: Vec = vec![100000000, 900000000]; -// add_network(1, 0, 0); -// assert_eq!( -// SubtensorModule::sudo_set_emission_values( -// <::RuntimeOrigin>::root(), -// netuids, -// emission -// ), -// Err(Error::::IncorrectNetuidsLength.into()) -// ); -// }); -// } - -// #[test] -// fn test_network_set_emission_ratios_fail_net() { -// new_test_ext().execute_with(|| { -// let netuids: Vec = vec![1, 2]; -// let emission: Vec = vec![100000000, 900000000]; -// add_network(1, 0, 0); -// add_network(3, 0, 0); -// assert_eq!( -// SubtensorModule::sudo_set_emission_values( -// <::RuntimeOrigin>::root(), -// netuids, -// emission -// ), -// Err(Error::::UidVecContainInvalidOne.into()) -// ); -// }); -// } - -// #[test] -// fn test_add_difficulty_fail() { -// new_test_ext().execute_with(|| { -// let netuid: u16 = 1; -// assert_eq!( -// SubtensorModule::sudo_set_difficulty( -// <::RuntimeOrigin>::root(), -// netuid, -// 120000 -// ), -// Err(Error::::NetworkDoesNotExist.into()) -// ); -// }); -// } - -// #[test] -// fn test_multi_tempo_with_emission() { -// new_test_ext().execute_with(|| { -// let netuid: u16 = 1; -// assert_eq!( -// SubtensorModule::sudo_set_difficulty( -// <::RuntimeOrigin>::root(), -// netuid, -// 120000 -// ), -// Err(Error::::NetworkDoesNotExist.into()) -// ); -// }); -// } - -// #[test] -// // Required by the test otherwise it would panic if compiled in debug mode -// #[allow(arithmetic_overflow)] -// fn test_set_emission_values_errors_on_emission_sum_overflow() { -// new_test_ext().execute_with(|| { -// let netuids: Vec = vec![1, 2]; -// // u64(u64::MAX + 1..000..1) equals to 1_000_000_000 which is the same as -// // the value of Self::get_block_emission() expected by the extrinsic -// let emission: Vec = vec![u64::MAX, 1_000_000_001]; -// add_network(1, 0, 0); -// add_network(2, 0, 0); -// assert_eq!( -// SubtensorModule::sudo_set_emission_values( -// <::RuntimeOrigin>::root(), -// netuids, -// emission -// ), -// Err(Error::::InvalidEmissionValues.into()) -// ); -// }); -// } - -// #[test] -// #[allow(arithmetic_overflow)] -// fn test_set_emission_values_no_errors() { -// new_test_ext().execute_with(|| { -// let netuids: Vec = vec![1, 2]; -// let emission: Vec = vec![600_000_000, 400_000_000]; - -// add_network(1, 0, 0); -// add_network(2, 0, 0); -// assert_eq!( -// SubtensorModule::sudo_set_emission_values( -// <::RuntimeOrigin>::root(), -// netuids, -// emission -// ), -// Ok(()) -// ); -// }); -// } - -// #[test] -// // Required by the test otherwise it would panic if compiled in debug mode -// #[allow(arithmetic_overflow)] -// fn test_set_emission_values_sum_too_large() { -// new_test_ext().execute_with(|| { -// let netuids: Vec = vec![1, 2]; -// // u64(1_000_000_000 + 1) equals to 1_000_000_001 which is more than -// // the value of Self::get_block_emission() expected by the extrinsic -// let emission: Vec = vec![1_000_000_000, 1]; -// add_network(1, 0, 0); -// add_network(2, 0, 0); -// assert_eq!( -// SubtensorModule::sudo_set_emission_values( -// <::RuntimeOrigin>::root(), -// netuids, -// emission -// ), -// Err(Error::::InvalidEmissionValues.into()) -// ); -// }); -// } - -// #[test] -// // Required by the test otherwise it would panic if compiled in debug mode -// #[allow(arithmetic_overflow)] -// fn test_set_emission_values_sum_too_small() { -// new_test_ext().execute_with(|| { -// let netuids: Vec = vec![1, 2]; -// // u64(1 + 2_000) equals to 2_001 which is LESS than -// // the value of Self::get_block_emission() expected by the extrinsic -// let emission: Vec = vec![1, 2_000]; -// add_network(1, 0, 0); -// add_network(2, 0, 0); -// assert_eq!( -// SubtensorModule::sudo_set_emission_values( -// <::RuntimeOrigin>::root(), -// netuids, -// emission -// ), -// Err(Error::::InvalidEmissionValues.into()) -// ); -// }); -// } - -// #[test] -// fn test_set_emission_values_too_many_netuids() { -// new_test_ext().execute_with(|| { -// let netuids: Vec = vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; - -// // Sums to 1_000_000_000 and has 10 elements -// let emission: Vec = vec![1_000_000_000, 0, 0, 0, 0, 0, 0, 0, 0, 0]; -// add_network(1, 0, 0); -// add_network(2, 0, 0); -// // We only add 2 networks, so this should fail -// assert_eq!( -// SubtensorModule::sudo_set_emission_values( -// <::RuntimeOrigin>::root(), -// netuids, -// emission -// ), -// Err(Error::::IncorrectNetuidsLength.into()) -// ); -// }); -// } - -// #[test] -// fn test_set_emission_values_over_u16_max_values() { -// new_test_ext().execute_with(|| { -// // Make vec of u16 with length 2^16 + 2 -// let netuids: Vec = vec![0; 0x10002]; -// // This is greater than u16::MAX -// assert!(netuids.len() > u16::MAX as usize); -// // On cast to u16, this will be 2 -// assert!(netuids.len() as u16 == 2); - -// // Sums to 1_000_000_000 and the length is 65536 -// let mut emission: Vec = vec![0; netuids.len()]; -// emission[0] = 1_000_000_000; - -// add_network(1, 0, 0); -// add_network(2, 0, 0); -// // We only add 2 networks, so this should fail -// // but if we cast to u16 during length comparison, -// // the length will be 2 and the check will pass -// assert_eq!( -// SubtensorModule::sudo_set_emission_values( -// <::RuntimeOrigin>::root(), -// netuids, -// emission -// ), -// Err(Error::::IncorrectNetuidsLength.into()) -// ); -// }); -// } +use crate::mock::*; +use frame_support::assert_ok; +use frame_system::Config; +use pallet_subtensor::{DissolveNetworkScheduleDuration, Event}; +use sp_core::U256; + +mod mock; + +#[test] +fn test_registration_ok() { + 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_ok!(SubtensorModule::user_remove_network( + <::RuntimeOrigin>::signed(coldkey_account_id), + netuid + )); + + 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)); + }) +} diff --git a/pallets/subtensor/tests/registration.rs b/pallets/subtensor/tests/registration.rs index 7d6e8ea65..9cea45bc5 100644 --- a/pallets/subtensor/tests/registration.rs +++ b/pallets/subtensor/tests/registration.rs @@ -276,7 +276,7 @@ fn test_registration_rate_limit_exceeded() { let result = extension.validate(&who, &call.into(), &info, 10); // Expectation: The transaction should be rejected - assert_err!(result, InvalidTransaction::ExhaustsResources); + assert_err!(result, InvalidTransaction::Custom(5)); let current_registrants = SubtensorModule::get_registrations_this_interval(netuid); assert!(current_registrants <= max_registrants); @@ -360,10 +360,7 @@ fn test_burned_registration_rate_limit_exceeded() { extension.validate(&who, &call_burned_register.into(), &info, 10); // Expectation: The transaction should be rejected - assert_err!( - burned_register_result, - InvalidTransaction::ExhaustsResources - ); + assert_err!(burned_register_result, InvalidTransaction::Custom(5)); let current_registrants = SubtensorModule::get_registrations_this_interval(netuid); assert!(current_registrants <= max_registrants); @@ -538,6 +535,122 @@ fn test_burn_adjustment() { }); } +#[test] +fn test_burn_registration_pruning_scenarios() { + new_test_ext(1).execute_with(|| { + let netuid: u16 = 1; + let tempo: u16 = 13; + let burn_cost = 1000; + let coldkey_account_id = U256::from(667); + let max_allowed_uids = 6; + let immunity_period = 5000; + + const IS_IMMUNE: bool = true; + const NOT_IMMUNE: bool = false; + + // Initial setup + SubtensorModule::set_burn(netuid, burn_cost); + SubtensorModule::set_max_allowed_uids(netuid, max_allowed_uids); + SubtensorModule::set_target_registrations_per_interval(netuid, max_allowed_uids); + SubtensorModule::set_immunity_period(netuid, immunity_period); + + add_network(netuid, tempo, 0); + + let mint_balance = burn_cost * u64::from(max_allowed_uids) + 1_000_000_000; + SubtensorModule::add_balance_to_coldkey_account(&coldkey_account_id, mint_balance); + + // Register first half of neurons + for i in 0..3 { + assert_ok!(SubtensorModule::burned_register( + <::RuntimeOrigin>::signed(coldkey_account_id), + netuid, + U256::from(i) + )); + step_block(1); + } + + // Note: pruning score is set to u16::MAX after getting neuron to prune + + // 1. Test if all immune neurons + assert_eq!(SubtensorModule::get_neuron_is_immune(netuid, 0), IS_IMMUNE); + assert_eq!(SubtensorModule::get_neuron_is_immune(netuid, 1), IS_IMMUNE); + assert_eq!(SubtensorModule::get_neuron_is_immune(netuid, 2), IS_IMMUNE); + + SubtensorModule::set_pruning_score_for_uid(netuid, 0, 100); + SubtensorModule::set_pruning_score_for_uid(netuid, 1, 75); + SubtensorModule::set_pruning_score_for_uid(netuid, 2, 50); + + // The immune neuron with the lowest score should be pruned + assert_eq!(SubtensorModule::get_neuron_to_prune(netuid), 2); + + // 2. Test tie-breaking for immune neurons + SubtensorModule::set_pruning_score_for_uid(netuid, 1, 50); + SubtensorModule::set_pruning_score_for_uid(netuid, 2, 50); + + // Should get the oldest neuron (i.e., neuron that was registered first) + assert_eq!(SubtensorModule::get_neuron_to_prune(netuid), 1); + + // 3. Test if no immune neurons + step_block(immunity_period); + + // ensure all neurons are non-immune + assert_eq!(SubtensorModule::get_neuron_is_immune(netuid, 0), NOT_IMMUNE); + assert_eq!(SubtensorModule::get_neuron_is_immune(netuid, 1), NOT_IMMUNE); + assert_eq!(SubtensorModule::get_neuron_is_immune(netuid, 2), NOT_IMMUNE); + + SubtensorModule::set_pruning_score_for_uid(netuid, 0, 100); + SubtensorModule::set_pruning_score_for_uid(netuid, 1, 50); + SubtensorModule::set_pruning_score_for_uid(netuid, 2, 75); + + // The non-immune neuron with the lowest score should be pruned + assert_eq!(SubtensorModule::get_neuron_to_prune(netuid), 1); + + // 4. Test tie-breaking for non-immune neurons + SubtensorModule::set_pruning_score_for_uid(netuid, 1, 50); + SubtensorModule::set_pruning_score_for_uid(netuid, 2, 50); + + // Should get the oldest non-immune neuron + assert_eq!(SubtensorModule::get_neuron_to_prune(netuid), 1); + + // 5. Test mixed immunity + // Register second batch of neurons (these will be non-immune) + for i in 3..6 { + assert_ok!(SubtensorModule::burned_register( + <::RuntimeOrigin>::signed(coldkey_account_id), + netuid, + U256::from(i) + )); + step_block(1); + } + + // Ensure all new neurons are immune + assert_eq!(SubtensorModule::get_neuron_is_immune(netuid, 3), IS_IMMUNE); + assert_eq!(SubtensorModule::get_neuron_is_immune(netuid, 4), IS_IMMUNE); + assert_eq!(SubtensorModule::get_neuron_is_immune(netuid, 5), IS_IMMUNE); + + // Set pruning scores for all neurons + SubtensorModule::set_pruning_score_for_uid(netuid, 0, 75); // non-immune + SubtensorModule::set_pruning_score_for_uid(netuid, 1, 50); // non-immune + SubtensorModule::set_pruning_score_for_uid(netuid, 2, 60); // non-immune + SubtensorModule::set_pruning_score_for_uid(netuid, 3, 40); // immune + SubtensorModule::set_pruning_score_for_uid(netuid, 4, 55); // immune + SubtensorModule::set_pruning_score_for_uid(netuid, 5, 45); // immune + + // The non-immune neuron with the lowest score should be pruned + assert_eq!(SubtensorModule::get_neuron_to_prune(netuid), 1); + + // If we remove the lowest non-immune neuron, it should choose the next lowest non-immune + SubtensorModule::set_pruning_score_for_uid(netuid, 1, u16::MAX); + assert_eq!(SubtensorModule::get_neuron_to_prune(netuid), 2); + + // If we make all non-immune neurons have high scores, it should choose the oldest non-immune neuron + SubtensorModule::set_pruning_score_for_uid(netuid, 0, u16::MAX); + SubtensorModule::set_pruning_score_for_uid(netuid, 1, u16::MAX); + SubtensorModule::set_pruning_score_for_uid(netuid, 2, u16::MAX); + assert_eq!(SubtensorModule::get_neuron_to_prune(netuid), 0); + }); +} + #[test] fn test_registration_too_many_registrations_per_block() { new_test_ext(1).execute_with(|| { diff --git a/pallets/subtensor/tests/serving.rs b/pallets/subtensor/tests/serving.rs index b87b7fd10..b0eada8e6 100644 --- a/pallets/subtensor/tests/serving.rs +++ b/pallets/subtensor/tests/serving.rs @@ -1,11 +1,14 @@ use crate::mock::*; mod mock; +use frame_support::assert_noop; +use frame_support::pallet_prelude::Weight; use frame_support::{ assert_ok, dispatch::{DispatchClass, DispatchInfo, GetDispatchInfo, Pays}, }; use frame_system::Config; use pallet_subtensor::Error; +use pallet_subtensor::*; use sp_core::U256; mod test { @@ -548,3 +551,279 @@ fn test_serving_is_invalid_ipv6_address() { )); }); } + +// SKIP_WASM_BUILD=1 RUST_LOG=debug cargo test --test serving -- test_do_set_identity --exact --nocapture +#[test] +fn test_do_set_identity() { + new_test_ext(1).execute_with(|| { + let coldkey = U256::from(1); + let hotkey = U256::from(2); + let netuid = 1; + + // Register a hotkey for the coldkey + add_network(netuid, 13, 0); + register_ok_neuron(netuid, hotkey, coldkey, 0); + + // Prepare identity data + let name = b"Alice".to_vec(); + let url = b"https://alice.com".to_vec(); + let image = b"alice.jpg".to_vec(); + let discord = b"alice#1234".to_vec(); + let description = b"Alice's identity".to_vec(); + let additional = b"Additional info".to_vec(); + + // Set identity + assert_ok!(SubtensorModule::do_set_identity( + <::RuntimeOrigin>::signed(coldkey), + name.clone(), + url.clone(), + image.clone(), + discord.clone(), + description.clone(), + additional.clone() + )); + + // Check if identity is set correctly + let stored_identity = Identities::::get(coldkey).expect("Identity should be set"); + assert_eq!(stored_identity.name, name); + assert_eq!(stored_identity.url, url); + assert_eq!(stored_identity.image, image); + assert_eq!(stored_identity.discord, discord); + assert_eq!(stored_identity.description, description); + assert_eq!(stored_identity.additional, additional); + + // Test setting identity with no registered hotkey + let coldkey_without_hotkey = U256::from(3); + assert_noop!( + SubtensorModule::do_set_identity( + <::RuntimeOrigin>::signed(coldkey_without_hotkey), + name.clone(), + url.clone(), + image.clone(), + discord.clone(), + description.clone(), + additional.clone() + ), + Error::::HotKeyNotRegisteredInNetwork + ); + + // Test updating an existing identity + let new_name = b"Alice Updated".to_vec(); + let new_url = b"https://alice-updated.com".to_vec(); + assert_ok!(SubtensorModule::do_set_identity( + <::RuntimeOrigin>::signed(coldkey), + new_name.clone(), + new_url.clone(), + image.clone(), + discord.clone(), + description.clone(), + additional.clone() + )); + + let updated_identity = + Identities::::get(coldkey).expect("Updated identity should be set"); + assert_eq!(updated_identity.name, new_name); + assert_eq!(updated_identity.url, new_url); + + // Test setting identity with invalid data (exceeding 512 bytes total) + let long_data = vec![0; 513]; + assert_noop!( + SubtensorModule::do_set_identity( + <::RuntimeOrigin>::signed(coldkey), + long_data.clone(), + long_data.clone(), + long_data.clone(), + long_data.clone(), + long_data.clone(), + long_data.clone() + ), + Error::::InvalidIdentity + ); + }); +} + +// SKIP_WASM_BUILD=1 RUST_LOG=debug cargo test --test serving -- test_is_valid_identity --exact --nocapture +#[test] +fn test_is_valid_identity() { + new_test_ext(1).execute_with(|| { + // Test valid identity + let valid_identity = ChainIdentity { + name: vec![0; 256], + url: vec![0; 256], + image: vec![0; 1024], + discord: vec![0; 256], + description: vec![0; 1024], + additional: vec![0; 1024], + }; + assert!(SubtensorModule::is_valid_identity(&valid_identity)); + + // Test identity with total length exactly at the maximum + let max_length_identity = ChainIdentity { + name: vec![0; 256], + url: vec![0; 256], + image: vec![0; 1024], + discord: vec![0; 256], + description: vec![0; 1024], + additional: vec![0; 1024], + }; + assert!(SubtensorModule::is_valid_identity(&max_length_identity)); + + // Test identity with total length exceeding the maximum + let invalid_length_identity = ChainIdentity { + name: vec![0; 257], + url: vec![0; 256], + image: vec![0; 1024], + discord: vec![0; 256], + description: vec![0; 1024], + additional: vec![0; 1024], + }; + assert!(!SubtensorModule::is_valid_identity( + &invalid_length_identity + )); + + // Test identity with one field exceeding its maximum + let invalid_field_identity = ChainIdentity { + name: vec![0; 257], + url: vec![0; 256], + image: vec![0; 1024], + discord: vec![0; 256], + description: vec![0; 1024], + additional: vec![0; 1024], + }; + assert!(!SubtensorModule::is_valid_identity(&invalid_field_identity)); + + // Test identity with empty fields + let empty_identity = ChainIdentity { + name: vec![], + url: vec![], + image: vec![], + discord: vec![], + description: vec![], + additional: vec![], + }; + assert!(SubtensorModule::is_valid_identity(&empty_identity)); + + // Test identity with some empty and some filled fields + let mixed_identity = ChainIdentity { + name: b"Alice".to_vec(), + url: b"https://alice.com".to_vec(), + image: vec![], + discord: b"alice#1234".to_vec(), + description: vec![], + additional: b"Additional info".to_vec(), + }; + assert!(SubtensorModule::is_valid_identity(&mixed_identity)); + + // Test identity with all fields at maximum allowed length + let max_field_identity = ChainIdentity { + name: vec![0; 256], + url: vec![0; 256], + image: vec![0; 1024], + discord: vec![0; 256], + description: vec![0; 1024], + additional: vec![0; 1024], + }; + assert!(SubtensorModule::is_valid_identity(&max_field_identity)); + }); +} + +// SKIP_WASM_BUILD=1 RUST_LOG=debug cargo test --test serving -- test_set_and_get_identity --exact --nocapture +#[test] +fn test_set_and_get_identity() { + new_test_ext(1).execute_with(|| { + let coldkey = U256::from(1); + let hotkey = U256::from(2); + let netuid = 1; + + // Register a hotkey for the coldkey + add_network(netuid, 13, 0); + register_ok_neuron(netuid, hotkey, coldkey, 0); + + // Prepare identity data + let name = b"Bob".to_vec(); + let url = b"https://bob.com".to_vec(); + let image = b"bob.jpg".to_vec(); + let discord = b"bob#5678".to_vec(); + let description = b"Bob's identity".to_vec(); + let additional = b"More about Bob".to_vec(); + + // Set identity + assert_ok!(SubtensorModule::do_set_identity( + <::RuntimeOrigin>::signed(coldkey), + name.clone(), + url.clone(), + image.clone(), + discord.clone(), + description.clone(), + additional.clone() + )); + + // Get and verify identity + let stored_identity = Identities::::get(coldkey).expect("Identity should be set"); + assert_eq!(stored_identity.name, name); + assert_eq!(stored_identity.url, url); + assert_eq!(stored_identity.image, image); + assert_eq!(stored_identity.discord, discord); + assert_eq!(stored_identity.description, description); + assert_eq!(stored_identity.additional, additional); + + // Update identity + let new_name = b"Bobby".to_vec(); + let new_url = b"https://bobby.com".to_vec(); + assert_ok!(SubtensorModule::do_set_identity( + <::RuntimeOrigin>::signed(coldkey), + new_name.clone(), + new_url.clone(), + image.clone(), + discord.clone(), + description.clone(), + additional.clone() + )); + + // Get and verify updated identity + let updated_identity = + Identities::::get(coldkey).expect("Updated identity should be set"); + assert_eq!(updated_identity.name, new_name); + assert_eq!(updated_identity.url, new_url); + assert_eq!(updated_identity.image, image); + assert_eq!(updated_identity.discord, discord); + assert_eq!(updated_identity.description, description); + assert_eq!(updated_identity.additional, additional); + + // Verify non-existent identity + let non_existent_coldkey = U256::from(999); + assert!(Identities::::get(non_existent_coldkey).is_none()); + }); +} + +// SKIP_WASM_BUILD=1 RUST_LOG=debug cargo test --test serving -- test_migrate_set_hotkey_identities --exact --nocapture +#[test] +fn test_migrate_set_hotkey_identities() { + new_test_ext(1).execute_with(|| { + // Run the migration + let weight = + pallet_subtensor::migrations::migrate_chain_identity::migrate_set_hotkey_identities::< + Test, + >(); + + // Assert that the migration has run + assert!(HasMigrationRun::::get(b"migrate_identities".to_vec())); + + // Verify that some identities were set + // Note: This assumes that at least one valid identity was in the JSON file + let mut identity_count = 0; + for (_, _) in Identities::::iter() { + identity_count += 1; + } + assert!( + identity_count > 0, + "No identities were set during migration" + ); + + // Verify that the weight is non-zero + assert!( + weight != Weight::zero(), + "Migration weight should be non-zero" + ); + }); +} diff --git a/pallets/subtensor/tests/staking.rs b/pallets/subtensor/tests/staking.rs index 5bf95841a..f053c7ca6 100644 --- a/pallets/subtensor/tests/staking.rs +++ b/pallets/subtensor/tests/staking.rs @@ -1395,7 +1395,7 @@ fn test_clear_small_nominations() { assert_ok!(SubtensorModule::do_become_delegate( <::RuntimeOrigin>::signed(cold1), hot1, - SubtensorModule::get_min_take() + SubtensorModule::get_min_delegate_take() )); assert_eq!(SubtensorModule::get_owning_coldkey_for_hotkey(&hot1), cold1); @@ -1404,7 +1404,7 @@ fn test_clear_small_nominations() { assert_ok!(SubtensorModule::do_become_delegate( <::RuntimeOrigin>::signed(cold2), hot2, - SubtensorModule::get_min_take() + SubtensorModule::get_min_delegate_take() )); assert_eq!(SubtensorModule::get_owning_coldkey_for_hotkey(&hot2), cold2); @@ -1697,11 +1697,11 @@ fn test_delegate_take_can_be_decreased() { assert_ok!(SubtensorModule::do_become_delegate( <::RuntimeOrigin>::signed(coldkey0), hotkey0, - SubtensorModule::get_min_take() + SubtensorModule::get_min_delegate_take() )); assert_eq!( SubtensorModule::get_hotkey_take(&hotkey0), - SubtensorModule::get_min_take() + SubtensorModule::get_min_delegate_take() ); // Coldkey / hotkey 0 decreases take to 5%. This should fail as the minimum take is 9% @@ -1743,11 +1743,11 @@ fn test_can_set_min_take_ok() { assert_ok!(SubtensorModule::do_decrease_take( <::RuntimeOrigin>::signed(coldkey0), hotkey0, - SubtensorModule::get_min_take() + SubtensorModule::get_min_delegate_take() )); assert_eq!( SubtensorModule::get_hotkey_take(&hotkey0), - SubtensorModule::get_min_take() + SubtensorModule::get_min_delegate_take() ); }); } @@ -1772,11 +1772,11 @@ fn test_delegate_take_can_not_be_increased_with_decrease_take() { assert_ok!(SubtensorModule::do_become_delegate( <::RuntimeOrigin>::signed(coldkey0), hotkey0, - SubtensorModule::get_min_take() + SubtensorModule::get_min_delegate_take() )); assert_eq!( SubtensorModule::get_hotkey_take(&hotkey0), - SubtensorModule::get_min_take() + SubtensorModule::get_min_delegate_take() ); // Coldkey / hotkey 0 tries to increase take to 12.5% @@ -1790,7 +1790,7 @@ fn test_delegate_take_can_not_be_increased_with_decrease_take() { ); assert_eq!( SubtensorModule::get_hotkey_take(&hotkey0), - SubtensorModule::get_min_take() + SubtensorModule::get_min_delegate_take() ); }); } @@ -1815,11 +1815,11 @@ fn test_delegate_take_can_be_increased() { assert_ok!(SubtensorModule::do_become_delegate( <::RuntimeOrigin>::signed(coldkey0), hotkey0, - SubtensorModule::get_min_take() + SubtensorModule::get_min_delegate_take() )); assert_eq!( SubtensorModule::get_hotkey_take(&hotkey0), - SubtensorModule::get_min_take() + SubtensorModule::get_min_delegate_take() ); step_block(1 + InitialTxDelegateTakeRateLimit::get() as u16); @@ -1854,11 +1854,11 @@ fn test_delegate_take_can_not_be_decreased_with_increase_take() { assert_ok!(SubtensorModule::do_become_delegate( <::RuntimeOrigin>::signed(coldkey0), hotkey0, - SubtensorModule::get_min_take() + SubtensorModule::get_min_delegate_take() )); assert_eq!( SubtensorModule::get_hotkey_take(&hotkey0), - SubtensorModule::get_min_take() + SubtensorModule::get_min_delegate_take() ); // Coldkey / hotkey 0 tries to decrease take to 5% @@ -1872,12 +1872,12 @@ fn test_delegate_take_can_not_be_decreased_with_increase_take() { ); assert_eq!( SubtensorModule::get_hotkey_take(&hotkey0), - SubtensorModule::get_min_take() + SubtensorModule::get_min_delegate_take() ); }); } -// Verify delegate take can be increased up to InitialDefaultTake (18%) +// Verify delegate take can be increased up to InitialDefaultDelegateTake (18%) #[test] fn test_delegate_take_can_be_increased_to_limit() { new_test_ext(1).execute_with(|| { @@ -1897,29 +1897,29 @@ fn test_delegate_take_can_be_increased_to_limit() { assert_ok!(SubtensorModule::do_become_delegate( <::RuntimeOrigin>::signed(coldkey0), hotkey0, - SubtensorModule::get_min_take() + SubtensorModule::get_min_delegate_take() )); assert_eq!( SubtensorModule::get_hotkey_take(&hotkey0), - SubtensorModule::get_min_take() + SubtensorModule::get_min_delegate_take() ); step_block(1 + InitialTxDelegateTakeRateLimit::get() as u16); - // Coldkey / hotkey 0 tries to increase take to InitialDefaultTake+1 + // Coldkey / hotkey 0 tries to increase take to InitialDefaultDelegateTake+1 assert_ok!(SubtensorModule::do_increase_take( <::RuntimeOrigin>::signed(coldkey0), hotkey0, - InitialDefaultTake::get() + InitialDefaultDelegateTake::get() )); assert_eq!( SubtensorModule::get_hotkey_take(&hotkey0), - InitialDefaultTake::get() + InitialDefaultDelegateTake::get() ); }); } -// Verify delegate take can not be set above InitialDefaultTake +// Verify delegate take can not be set above InitialDefaultDelegateTake #[test] fn test_delegate_take_can_not_be_set_beyond_limit() { new_test_ext(1).execute_with(|| { @@ -1937,13 +1937,13 @@ fn test_delegate_take_can_not_be_set_beyond_limit() { let before = SubtensorModule::get_hotkey_take(&hotkey0); // Coldkey / hotkey 0 attempt to become delegates with take above maximum - // (Disable this check if InitialDefaultTake is u16::MAX) - if InitialDefaultTake::get() != u16::MAX { + // (Disable this check if InitialDefaultDelegateTake is u16::MAX) + if InitialDefaultDelegateTake::get() != u16::MAX { assert_eq!( SubtensorModule::do_become_delegate( <::RuntimeOrigin>::signed(coldkey0), hotkey0, - InitialDefaultTake::get() + 1 + InitialDefaultDelegateTake::get() + 1 ), Err(Error::::DelegateTakeTooHigh.into()) ); @@ -1952,7 +1952,7 @@ fn test_delegate_take_can_not_be_set_beyond_limit() { }); } -// Verify delegate take can not be increased above InitialDefaultTake (18%) +// Verify delegate take can not be increased above InitialDefaultDelegateTake (18%) #[test] fn test_delegate_take_can_not_be_increased_beyond_limit() { new_test_ext(1).execute_with(|| { @@ -1972,28 +1972,28 @@ fn test_delegate_take_can_not_be_increased_beyond_limit() { assert_ok!(SubtensorModule::do_become_delegate( <::RuntimeOrigin>::signed(coldkey0), hotkey0, - SubtensorModule::get_min_take() + SubtensorModule::get_min_delegate_take() )); assert_eq!( SubtensorModule::get_hotkey_take(&hotkey0), - SubtensorModule::get_min_take() + SubtensorModule::get_min_delegate_take() ); - // Coldkey / hotkey 0 tries to increase take to InitialDefaultTake+1 - // (Disable this check if InitialDefaultTake is u16::MAX) - if InitialDefaultTake::get() != u16::MAX { + // Coldkey / hotkey 0 tries to increase take to InitialDefaultDelegateTake+1 + // (Disable this check if InitialDefaultDelegateTake is u16::MAX) + if InitialDefaultDelegateTake::get() != u16::MAX { assert_eq!( SubtensorModule::do_increase_take( <::RuntimeOrigin>::signed(coldkey0), hotkey0, - InitialDefaultTake::get() + 1 + InitialDefaultDelegateTake::get() + 1 ), Err(Error::::DelegateTakeTooHigh.into()) ); } assert_eq!( SubtensorModule::get_hotkey_take(&hotkey0), - SubtensorModule::get_min_take() + SubtensorModule::get_min_delegate_take() ); }); } @@ -2018,11 +2018,11 @@ fn test_rate_limits_enforced_on_increase_take() { assert_ok!(SubtensorModule::do_become_delegate( <::RuntimeOrigin>::signed(coldkey0), hotkey0, - SubtensorModule::get_min_take() + SubtensorModule::get_min_delegate_take() )); assert_eq!( SubtensorModule::get_hotkey_take(&hotkey0), - SubtensorModule::get_min_take() + SubtensorModule::get_min_delegate_take() ); // Coldkey / hotkey 0 increases take to 12.5% @@ -2036,7 +2036,7 @@ fn test_rate_limits_enforced_on_increase_take() { ); assert_eq!( SubtensorModule::get_hotkey_take(&hotkey0), - SubtensorModule::get_min_take() + SubtensorModule::get_min_delegate_take() ); step_block(1 + InitialTxDelegateTakeRateLimit::get() as u16); diff --git a/pallets/subtensor/tests/swap_coldkey.rs b/pallets/subtensor/tests/swap_coldkey.rs index 9203e2b3f..ec030ebee 100644 --- a/pallets/subtensor/tests/swap_coldkey.rs +++ b/pallets/subtensor/tests/swap_coldkey.rs @@ -4,10 +4,16 @@ use frame_support::weights::Weight; use frame_support::{assert_err, assert_noop, assert_ok}; use frame_system::{Config, RawOrigin}; mod mock; +use frame_support::error::BadOrigin; +use frame_support::traits::schedule::v3::Named as ScheduleNamed; +use frame_support::traits::schedule::DispatchTime; +use frame_support::traits::OnInitialize; use mock::*; use pallet_subtensor::*; +use pallet_subtensor::{Call, ColdkeySwapScheduleDuration, Error}; use sp_core::H256; use sp_core::U256; +use sp_runtime::DispatchError; // SKIP_WASM_BUILD=1 RUST_LOG=debug cargo test --test swap_coldkey -- test_swap_total_hotkey_coldkey_stakes_this_interval --exact --nocapture #[test] @@ -542,7 +548,8 @@ fn test_do_swap_coldkey_success() { // Perform the swap assert_ok!(SubtensorModule::do_swap_coldkey( - <::RuntimeOrigin>::signed(old_coldkey), + // <::RuntimeOrigin>::signed(old_coldkey), + &old_coldkey, &new_coldkey )); @@ -882,10 +889,7 @@ fn test_do_swap_coldkey_with_subnet_ownership() { OwnedHotkeys::::insert(old_coldkey, vec![hotkey]); // Perform the swap - assert_ok!(SubtensorModule::do_swap_coldkey( - <::RuntimeOrigin>::signed(old_coldkey), - &new_coldkey - )); + assert_ok!(SubtensorModule::do_swap_coldkey(&old_coldkey, &new_coldkey)); // Verify subnet ownership transfer assert_eq!(SubnetOwner::::get(netuid), new_coldkey); @@ -1286,3 +1290,193 @@ fn test_coldkey_delegations() { assert_eq!(Stake::::get(delegate, coldkey), 0); }); } + +#[test] +fn test_schedule_swap_coldkey_success() { + new_test_ext(1).execute_with(|| { + // Initialize test accounts + let old_coldkey: U256 = U256::from(1); + let new_coldkey: U256 = U256::from(2); + + // Add balance to the old coldkey account + SubtensorModule::add_balance_to_coldkey_account(&old_coldkey, 1000); + + // Schedule the coldkey swap + assert_ok!(SubtensorModule::schedule_swap_coldkey( + <::RuntimeOrigin>::signed(old_coldkey), + new_coldkey + )); + + // Get the current block number + let current_block: u64 = System::block_number(); + + // Calculate the expected execution block (5 days from now) + let expected_execution_block: u64 = current_block + 5 * 24 * 60 * 60 / 12; + + // Check for the SwapScheduled event + System::assert_last_event( + Event::ColdkeySwapScheduled { + old_coldkey, + new_coldkey, + execution_block: expected_execution_block, + } + .into(), + ); + + // TODO: Add additional checks to ensure the swap is correctly scheduled in the system + // For example, verify that the swap is present in the appropriate storage or scheduler + }); +} + +// SKIP_WASM_BUILD=1 RUST_LOG=info cargo test --test swap_coldkey -- test_schedule_swap_coldkey_duplicate --exact --nocapture +#[test] +fn test_schedule_swap_coldkey_duplicate() { + new_test_ext(1).execute_with(|| { + let old_coldkey = U256::from(1); + let new_coldkey = U256::from(2); + + SubtensorModule::add_balance_to_coldkey_account(&old_coldkey, 2000); + + assert_ok!(SubtensorModule::schedule_swap_coldkey( + <::RuntimeOrigin>::signed(old_coldkey), + new_coldkey + )); + + // Attempt to schedule again + assert_noop!( + SubtensorModule::schedule_swap_coldkey( + <::RuntimeOrigin>::signed(old_coldkey), + new_coldkey + ), + Error::::SwapAlreadyScheduled + ); + }); +} + +// SKIP_WASM_BUILD=1 RUST_LOG=info cargo test --test swap_coldkey -- test_schedule_swap_coldkey_execution --exact --nocapture +#[test] +fn test_schedule_swap_coldkey_execution() { + new_test_ext(1).execute_with(|| { + let old_coldkey = U256::from(1); + let new_coldkey = U256::from(2); + let hotkey = U256::from(3); + let netuid = 1u16; + let stake_amount = 100; + + add_network(netuid, 13, 0); + register_ok_neuron(netuid, hotkey, old_coldkey, 0); + SubtensorModule::add_balance_to_coldkey_account(&old_coldkey, 1000000000000000); + assert_ok!(SubtensorModule::add_stake( + <::RuntimeOrigin>::signed(old_coldkey), + hotkey, + stake_amount + )); + + // Check initial ownership + assert_eq!( + Owner::::get(hotkey), + old_coldkey, + "Initial ownership check failed" + ); + + // Schedule the swap + assert_ok!(SubtensorModule::schedule_swap_coldkey( + <::RuntimeOrigin>::signed(old_coldkey), + new_coldkey + )); + + // Get the scheduled execution block + let current_block = System::block_number(); + let execution_block = current_block + ColdkeySwapScheduleDuration::::get(); + + System::assert_last_event( + Event::ColdkeySwapScheduled { + old_coldkey, + new_coldkey, + execution_block, + } + .into(), + ); + + run_to_block(execution_block); + + // Run on_initialize for the execution block + SubtensorModule::on_initialize(execution_block); + + // Also run Scheduler's on_initialize + as OnInitialize>::on_initialize( + execution_block, + ); + + // Check if the swap has occurred + let new_owner = Owner::::get(hotkey); + assert_eq!( + new_owner, new_coldkey, + "Ownership was not updated as expected" + ); + + assert_eq!( + Stake::::get(hotkey, new_coldkey), + stake_amount, + "Stake was not transferred to new coldkey" + ); + assert_eq!( + Stake::::get(hotkey, old_coldkey), + 0, + "Old coldkey still has stake" + ); + + // Check for the SwapExecuted event + System::assert_has_event( + Event::ColdkeySwapped { + old_coldkey, + new_coldkey, + } + .into(), + ); + }); +} + +// SKIP_WASM_BUILD=1 RUST_LOG=info cargo test --test swap_coldkey -- test_direct_swap_coldkey_call_fails --exact --nocapture +#[test] +fn test_direct_swap_coldkey_call_fails() { + new_test_ext(1).execute_with(|| { + let old_coldkey = U256::from(1); + let new_coldkey = U256::from(2); + + assert_noop!( + SubtensorModule::swap_coldkey( + <::RuntimeOrigin>::signed(old_coldkey), + old_coldkey, + new_coldkey + ), + BadOrigin + ); + }); +} + +// SKIP_WASM_BUILD=1 RUST_LOG=info cargo test --test swap_coldkey -- test_schedule_swap_coldkey_with_pending_swap --exact --nocapture +#[test] +fn test_schedule_swap_coldkey_with_pending_swap() { + new_test_ext(1).execute_with(|| { + let old_coldkey = U256::from(1); + let new_coldkey1 = U256::from(2); + let new_coldkey2 = U256::from(3); + + SubtensorModule::add_balance_to_coldkey_account(&old_coldkey, 2000); + + assert_ok!(SubtensorModule::schedule_swap_coldkey( + <::RuntimeOrigin>::signed(old_coldkey), + new_coldkey1 + )); + + // Attempt to schedule another swap before the first one executes + assert_noop!( + SubtensorModule::schedule_swap_coldkey( + <::RuntimeOrigin>::signed(old_coldkey), + new_coldkey2 + ), + Error::::SwapAlreadyScheduled + ); + }); +} diff --git a/pallets/subtensor/tests/swap_hotkey.rs b/pallets/subtensor/tests/swap_hotkey.rs index b416e6b4d..bff738b86 100644 --- a/pallets/subtensor/tests/swap_hotkey.rs +++ b/pallets/subtensor/tests/swap_hotkey.rs @@ -960,164 +960,158 @@ fn test_swap_hotkey_error_cases() { }); } -// SKIP_WASM_BUILD=1 RUST_LOG=debug cargo test --test swap_hotkey -- test_swap_hotkey_does_not_become_delegate --exact --nocapture +// SKIP_WASM_BUILD=1 RUST_LOG=debug cargo test --test swap_hotkey -- test_swap_child_keys --exact --nocapture #[test] -fn test_swap_hotkey_does_not_become_delegate() { +fn test_swap_child_keys() { new_test_ext(1).execute_with(|| { - let netuid: u16 = 1; - let tempo: u16 = 13; let old_hotkey = U256::from(1); let new_hotkey = U256::from(2); let coldkey = U256::from(3); - let swap_cost = 1_000_000_000u64 * 2; - let delegate_take = 10u16; + let netuid = 0u16; + let children = vec![(100u64, U256::from(4)), (200u64, U256::from(5))]; + let mut weight = Weight::zero(); - // Setup initial state - add_network(netuid, tempo, 0); - register_ok_neuron(netuid, old_hotkey, coldkey, 0); - SubtensorModule::add_balance_to_coldkey_account(&coldkey, swap_cost); - - // Ensure old_hotkey is not a delegate - assert!(!Delegates::::contains_key(old_hotkey)); + // Initialize ChildKeys for old_hotkey + add_network(netuid, 1, 0); + ChildKeys::::insert(old_hotkey, netuid, children.clone()); // Perform the swap - assert_ok!(SubtensorModule::do_swap_hotkey( - <::RuntimeOrigin>::signed(coldkey), - &old_hotkey, - &new_hotkey - )); - - // Check that old_hotkey is still not a delegate - assert!(!Delegates::::contains_key(old_hotkey)); + SubtensorModule::perform_hotkey_swap(&old_hotkey, &new_hotkey, &coldkey, &mut weight); - // Check that new_hotkey is NOT a delegate either - assert!(!Delegates::::contains_key(new_hotkey)); + // Verify the swap + assert_eq!(ChildKeys::::get(new_hotkey, netuid), children); + assert!(ChildKeys::::get(old_hotkey, netuid).is_empty()); }); } -// SKIP_WASM_BUILD=1 RUST_LOG=debug cargo test --test swap_hotkey -- test_swap_hotkey_with_delegate_and_stakes --exact --nocapture +// SKIP_WASM_BUILD=1 RUST_LOG=debug cargo test --test swap_hotkey -- test_swap_parent_keys --exact --nocapture #[test] -fn test_swap_hotkey_with_delegate_and_stakes() { +fn test_swap_parent_keys() { new_test_ext(1).execute_with(|| { - let netuid: u16 = 1; - let tempo: u16 = 13; let old_hotkey = U256::from(1); let new_hotkey = U256::from(2); let coldkey = U256::from(3); - let staker1 = U256::from(4); - let staker2 = U256::from(5); - let swap_cost = 1_000_000_000u64; - let delegate_take = 11_796; - let stake_amount1 = 100u64; - let stake_amount2 = 200u64; + let netuid = 0u16; + let parents = vec![(100u64, U256::from(4)), (200u64, U256::from(5))]; + let mut weight = Weight::zero(); - // Setup initial state - add_network(netuid, tempo, 0); - register_ok_neuron(netuid, old_hotkey, coldkey, 0); - SubtensorModule::add_balance_to_coldkey_account(&coldkey, swap_cost); - SubtensorModule::add_balance_to_coldkey_account(&staker1, stake_amount1); - SubtensorModule::add_balance_to_coldkey_account(&staker2, stake_amount2); + // Initialize ParentKeys for old_hotkey + add_network(netuid, 1, 0); + ParentKeys::::insert(old_hotkey, netuid, parents.clone()); - // Make old_hotkey a delegate - assert_ok!(SubtensorModule::become_delegate( - RuntimeOrigin::signed(coldkey), - old_hotkey, - )); + // Initialize ChildKeys for parent + ChildKeys::::insert(U256::from(4), netuid, vec![(100u64, old_hotkey)]); + ChildKeys::::insert(U256::from(5), netuid, vec![(200u64, old_hotkey)]); - // Add stakes to the delegate - assert_ok!(SubtensorModule::add_stake( - RuntimeOrigin::signed(staker1), - old_hotkey, - stake_amount1 - )); - assert_ok!(SubtensorModule::add_stake( - RuntimeOrigin::signed(staker2), - old_hotkey, - stake_amount2 - )); + // Perform the swap + SubtensorModule::perform_hotkey_swap(&old_hotkey, &new_hotkey, &coldkey, &mut weight); + + // Verify ParentKeys swap + assert_eq!(ParentKeys::::get(new_hotkey, netuid), parents); + assert!(ParentKeys::::get(old_hotkey, netuid).is_empty()); - // Assert initial stake amounts + // Verify ChildKeys update for parents assert_eq!( - SubtensorModule::get_total_stake_for_hotkey(&old_hotkey), - stake_amount1 + stake_amount2 - (ExistentialDeposit::get() * 2) + ChildKeys::::get(U256::from(4), netuid), + vec![(100u64, new_hotkey)] ); - - // Print entire staking hotkeys map - log::info!( - "StakingHotkeys before swap: {:?}", - StakingHotkeys::::iter().collect::>() + assert_eq!( + ChildKeys::::get(U256::from(5), netuid), + vec![(200u64, new_hotkey)] ); + }); +} + +// SKIP_WASM_BUILD=1 RUST_LOG=debug cargo test --test swap_hotkey -- test_swap_multiple_subnets --exact --nocapture +#[test] +fn test_swap_multiple_subnets() { + new_test_ext(1).execute_with(|| { + let old_hotkey = U256::from(1); + let new_hotkey = U256::from(2); + let coldkey = U256::from(3); + let netuid1 = 0u16; + let netuid2 = 1u16; + let children1 = vec![(100u64, U256::from(4)), (200u64, U256::from(5))]; + let children2 = vec![(300u64, U256::from(6))]; + let mut weight = Weight::zero(); + + add_network(netuid1, 1, 0); + add_network(netuid2, 1, 0); + + // Initialize ChildKeys for old_hotkey in multiple subnets + ChildKeys::::insert(old_hotkey, netuid1, children1.clone()); + ChildKeys::::insert(old_hotkey, netuid2, children2.clone()); // Perform the swap - assert_ok!(SubtensorModule::do_swap_hotkey( - <::RuntimeOrigin>::signed(coldkey), - &old_hotkey, - &new_hotkey - )); + SubtensorModule::perform_hotkey_swap(&old_hotkey, &new_hotkey, &coldkey, &mut weight); - // Check that new_hotkey is now a delegate with the same take - assert_eq!(Delegates::::get(new_hotkey), delegate_take); - assert!(!Delegates::::contains_key(old_hotkey)); + // Verify the swap for both subnets + assert_eq!(ChildKeys::::get(new_hotkey, netuid1), children1); + assert_eq!(ChildKeys::::get(new_hotkey, netuid2), children2); + assert!(ChildKeys::::get(old_hotkey, netuid1).is_empty()); + assert!(ChildKeys::::get(old_hotkey, netuid2).is_empty()); + }); +} - // Check that stakes have been transferred to the new hotkey - assert_eq!( - SubtensorModule::get_total_stake_for_hotkey(&new_hotkey), - stake_amount1 + stake_amount2 - (ExistentialDeposit::get() * 2) +// SKIP_WASM_BUILD=1 RUST_LOG=debug cargo test --test swap_hotkey -- test_swap_complex_parent_child_structure --exact --nocapture +#[test] +fn test_swap_complex_parent_child_structure() { + new_test_ext(1).execute_with(|| { + let old_hotkey = U256::from(1); + let new_hotkey = U256::from(2); + let coldkey = U256::from(3); + let netuid = 0u16; + let parent1 = U256::from(4); + let parent2 = U256::from(5); + let child1 = U256::from(6); + let child2 = U256::from(7); + let mut weight = Weight::zero(); + + add_network(netuid, 1, 0); + + // Set up complex parent-child structure + ParentKeys::::insert( + old_hotkey, + netuid, + vec![(100u64, parent1), (200u64, parent2)], + ); + ChildKeys::::insert(old_hotkey, netuid, vec![(300u64, child1), (400u64, child2)]); + ChildKeys::::insert( + parent1, + netuid, + vec![(100u64, old_hotkey), (500u64, U256::from(8))], + ); + ChildKeys::::insert( + parent2, + netuid, + vec![(200u64, old_hotkey), (600u64, U256::from(9))], ); - assert_eq!(SubtensorModule::get_total_stake_for_hotkey(&old_hotkey), 0); + // Perform the swap + SubtensorModule::perform_hotkey_swap(&old_hotkey, &new_hotkey, &coldkey, &mut weight); - // Check that the total stake for the new hotkey is correct + // Verify ParentKeys swap assert_eq!( - TotalHotkeyStake::::get(new_hotkey), - stake_amount1 + stake_amount2 - (ExistentialDeposit::get() * 2) + ParentKeys::::get(new_hotkey, netuid), + vec![(100u64, parent1), (200u64, parent2)] ); - assert!(!TotalHotkeyStake::::contains_key(old_hotkey)); + assert!(ParentKeys::::get(old_hotkey, netuid).is_empty()); - // Print entire staking hotkeys map - log::info!( - "StakingHotkeys after swap: {:?}", - StakingHotkeys::::iter().collect::>() + // Verify ChildKeys swap + assert_eq!( + ChildKeys::::get(new_hotkey, netuid), + vec![(300u64, child1), (400u64, child2)] ); + assert!(ChildKeys::::get(old_hotkey, netuid).is_empty()); - // Check that the staking hotkeys for the stakers have been updated - assert!(StakingHotkeys::::get(staker1).contains(&new_hotkey)); - assert!(StakingHotkeys::::get(staker2).contains(&new_hotkey)); - assert!(!StakingHotkeys::::get(staker1).contains(&old_hotkey)); - assert!(!StakingHotkeys::::get(staker2).contains(&old_hotkey)); - - // Check staking hotkeys for new hotkey - // Retrieve all stakers associated with the new hotkey - let new_hotkey_stakers = StakingHotkeys::::iter() - // Iterate through all entries in the StakingHotkeys storage map - .filter(|(_, hotkeys)| hotkeys.contains(&new_hotkey)) - // Keep only entries where the new_hotkey is in the list of hotkeys - .map(|(staker, _)| staker) - // Extract just the staker (coldkey) from each matching entry - .collect::>(); - // Collect the results into a vector - - log::info!("new_hotkey_stakers: {:?}", new_hotkey_stakers); - - assert_eq!(new_hotkey_stakers.len(), 3); - assert!(new_hotkey_stakers.contains(&staker1)); - assert!(new_hotkey_stakers.contains(&staker2)); - - // Verify that old_hotkey is not in any staker's StakingHotkeys - let old_hotkey_stakers = StakingHotkeys::::iter() - .filter(|(_, hotkeys)| hotkeys.contains(&old_hotkey)) - .count(); - - assert_eq!(old_hotkey_stakers, 0); - - // Check that the total balances of stakers haven't changed + // Verify parent's ChildKeys update assert_eq!( - SubtensorModule::get_coldkey_balance(&staker1), - ExistentialDeposit::get() + ChildKeys::::get(parent1, netuid), + vec![(100u64, new_hotkey), (500u64, U256::from(8))] ); assert_eq!( - SubtensorModule::get_coldkey_balance(&staker2), - ExistentialDeposit::get() + ChildKeys::::get(parent2, netuid), + vec![(200u64, new_hotkey), (600u64, U256::from(9))] ); }); } diff --git a/pallets/subtensor/tests/weights.rs b/pallets/subtensor/tests/weights.rs index 020eb1f6b..214e3add0 100644 --- a/pallets/subtensor/tests/weights.rs +++ b/pallets/subtensor/tests/weights.rs @@ -107,7 +107,7 @@ fn test_set_rootweights_validate() { assert_err!( // Should get an invalid transaction error result_no_stake, - TransactionValidityError::Invalid(InvalidTransaction::Call,) + TransactionValidityError::Invalid(InvalidTransaction::Custom(4)) ); // Increase the stake to be equal to the minimum @@ -207,7 +207,7 @@ fn test_commit_weights_validate() { assert_err!( // Should get an invalid transaction error result_no_stake, - TransactionValidityError::Invalid(InvalidTransaction::Call,) + TransactionValidityError::Invalid(InvalidTransaction::Custom(1)) ); // Increase the stake to be equal to the minimum @@ -259,6 +259,66 @@ fn test_reveal_weights_dispatch_info_ok() { }); } +#[test] +fn test_set_weights_validate() { + // Testing the signed extension validate function + // correctly filters the `set_weights` transaction. + + new_test_ext(0).execute_with(|| { + let netuid: u16 = 1; + let coldkey = U256::from(0); + let hotkey: U256 = U256::from(1); + assert_ne!(hotkey, coldkey); + + let who = hotkey; // The hotkey signs this transaction + + let call = RuntimeCall::SubtensorModule(SubtensorCall::set_weights { + netuid, + dests: vec![1, 1], + weights: vec![1, 1], + version_key: 0, + }); + + // Create netuid + add_network(netuid, 0, 0); + // Register the hotkey + SubtensorModule::append_neuron(netuid, &hotkey, 0); + Owner::::insert(hotkey, coldkey); + + let min_stake = 500_000_000_000; + // Set the minimum stake + SubtensorModule::set_weights_min_stake(min_stake); + + // Verify stake is less than minimum + assert!(SubtensorModule::get_total_stake_for_hotkey(&hotkey) < min_stake); + let info: DispatchInfo = + DispatchInfoOf::<::RuntimeCall>::default(); + + let extension = pallet_subtensor::SubtensorSignedExtension::::new(); + // Submit to the signed extension validate function + let result_no_stake = extension.validate(&who, &call.clone(), &info, 10); + // Should fail due to insufficient stake + assert_err!( + result_no_stake, + TransactionValidityError::Invalid(InvalidTransaction::Custom(3)) + ); + + // Increase the stake to be equal to the minimum + SubtensorModule::increase_stake_on_hotkey_account(&hotkey, min_stake); + + // Verify stake is equal to minimum + assert_eq!( + SubtensorModule::get_total_stake_for_hotkey(&hotkey), + min_stake + ); + + // Submit to the signed extension validate function + let result_min_stake = extension.validate(&who, &call.clone(), &info, 10); + // Now the call should pass + assert_ok!(result_min_stake); + }); +} + #[test] fn test_reveal_weights_validate() { // Testing the signed extension validate function @@ -306,7 +366,7 @@ fn test_reveal_weights_validate() { assert_err!( // Should get an invalid transaction error result_no_stake, - TransactionValidityError::Invalid(InvalidTransaction::Call,) + TransactionValidityError::Invalid(InvalidTransaction::Custom(2)) ); // Increase the stake to be equal to the minimum diff --git a/runtime/Cargo.toml b/runtime/Cargo.toml index 60c8fa22e..8a2886eb1 100644 --- a/runtime/Cargo.toml +++ b/runtime/Cargo.toml @@ -155,7 +155,7 @@ std = [ "sp-tracing/std", "log/std", "sp-storage/std", - "sp-genesis-builder/std" + "sp-genesis-builder/std", ] runtime-benchmarks = [ "frame-benchmarking/runtime-benchmarks", @@ -178,7 +178,7 @@ runtime-benchmarks = [ "pallet-multisig/runtime-benchmarks", "pallet-preimage/runtime-benchmarks", "pallet-scheduler/runtime-benchmarks", - "pallet-sudo/runtime-benchmarks" + "pallet-sudo/runtime-benchmarks", ] try-runtime = [ "frame-try-runtime/try-runtime", @@ -204,6 +204,6 @@ try-runtime = [ "sp-runtime/try-runtime", "pallet-admin-utils/try-runtime", "pallet-commitments/try-runtime", - "pallet-registry/try-runtime" + "pallet-registry/try-runtime", ] metadata-hash = ["substrate-wasm-builder/metadata-hash"] diff --git a/runtime/src/lib.rs b/runtime/src/lib.rs index 49c9bb786..e6c0e9d32 100644 --- a/runtime/src/lib.rs +++ b/runtime/src/lib.rs @@ -12,13 +12,15 @@ pub mod check_nonce; mod migrations; use codec::{Decode, Encode, MaxEncodedLen}; +use frame_support::traits::Imbalance; use frame_support::{ dispatch::DispatchResultWithPostInfo, genesis_builder_helper::{build_config, create_default_config}, - pallet_prelude::{DispatchError, Get}, - traits::{fungible::HoldConsideration, Contains, LinearStoragePrice}, + pallet_prelude::Get, + traits::{fungible::HoldConsideration, Contains, LinearStoragePrice, OnUnbalanced}, }; use frame_system::{EnsureNever, EnsureRoot, EnsureRootWithSuccess, RawOrigin}; +use pallet_balances::NegativeImbalance; use pallet_commitments::CanCommit; use pallet_grandpa::{ fg_primitives, AuthorityId as GrandpaId, AuthorityList as GrandpaAuthorityList, @@ -68,6 +70,7 @@ pub use sp_runtime::BuildStorage; pub use sp_runtime::{Perbill, Permill}; // Subtensor module +pub use pallet_scheduler; pub use pallet_subtensor; // An index to a block. @@ -388,11 +391,22 @@ parameter_types! { pub FeeMultiplier: Multiplier = Multiplier::one(); } +/// Deduct the transaction fee from the Subtensor Pallet TotalIssuance when dropping the transaction +/// fee. +pub struct TransactionFeeHandler; +impl OnUnbalanced> for TransactionFeeHandler { + fn on_nonzero_unbalanced(credit: NegativeImbalance) { + let ti_before = pallet_subtensor::TotalIssuance::::get(); + pallet_subtensor::TotalIssuance::::put(ti_before.saturating_sub(credit.peek())); + drop(credit); + } +} + impl pallet_transaction_payment::Config for Runtime { type RuntimeEvent = RuntimeEvent; - type OnChargeTransaction = CurrencyAdapter; //type TransactionByteFee = TransactionByteFee; + type OnChargeTransaction = CurrencyAdapter; // Convert dispatch weight to a chargeable fee. type WeightToFee = LinearWeightToFee; @@ -613,7 +627,11 @@ pub enum ProxyType { Governance, // Both above governance Staking, Registration, + Transfer, + SmallTransfer, } +// Transfers below SMALL_TRANSFER_LIMIT are considered small transfers +pub const SMALL_TRANSFER_LIMIT: Balance = 500_000_000; // 0.5 TAO impl Default for ProxyType { fn default() -> Self { Self::Any @@ -632,6 +650,22 @@ impl InstanceFilter for ProxyType { | RuntimeCall::SubtensorModule(pallet_subtensor::Call::burned_register { .. }) | RuntimeCall::SubtensorModule(pallet_subtensor::Call::root_register { .. }) ), + ProxyType::Transfer => matches!( + c, + RuntimeCall::Balances(pallet_balances::Call::transfer_keep_alive { .. }) + | RuntimeCall::Balances(pallet_balances::Call::transfer_allow_death { .. }) + | RuntimeCall::Balances(pallet_balances::Call::transfer_all { .. }) + ), + ProxyType::SmallTransfer => match c { + RuntimeCall::Balances(pallet_balances::Call::transfer_keep_alive { + value, .. + }) => *value < SMALL_TRANSFER_LIMIT, + RuntimeCall::Balances(pallet_balances::Call::transfer_allow_death { + value, + .. + }) => *value < SMALL_TRANSFER_LIMIT, + _ => false, + }, ProxyType::Owner => matches!(c, RuntimeCall::AdminUtils(..)), ProxyType::NonCritical => !matches!( c, @@ -668,8 +702,12 @@ impl InstanceFilter for ProxyType { (x, y) if x == y => true, (ProxyType::Any, _) => true, (_, ProxyType::Any) => false, - (ProxyType::NonTransfer, _) => true, + (ProxyType::NonTransfer, _) => { + // NonTransfer is NOT a superset of Transfer or SmallTransfer + !matches!(o, ProxyType::Transfer | ProxyType::SmallTransfer) + } (ProxyType::Governance, ProxyType::Triumvirate | ProxyType::Senate) => true, + (ProxyType::Transfer, ProxyType::SmallTransfer) => true, _ => false, } } @@ -835,6 +873,18 @@ impl pallet_commitments::Config for Runtime { type RateLimit = CommitmentRateLimit; } +#[cfg(not(feature = "fast-blocks"))] +pub const INITIAL_SUBNET_TEMPO: u16 = 99; + +#[cfg(feature = "fast-blocks")] +pub const INITIAL_SUBNET_TEMPO: u16 = 10; + +#[cfg(not(feature = "fast-blocks"))] +pub const INITIAL_CHILDKEY_TAKE_RATELIMIT: u64 = 216000; // 30 days at 12 seconds per block + +#[cfg(feature = "fast-blocks")] +pub const INITIAL_CHILDKEY_TAKE_RATELIMIT: u64 = 5; + // Configure the pallet subtensor. parameter_types! { pub const SubtensorInitialRho: u16 = 10; @@ -847,7 +897,7 @@ parameter_types! { pub const SubtensorInitialValidatorPruneLen: u64 = 1; pub const SubtensorInitialScalingLawPower: u16 = 50; // 0.5 pub const SubtensorInitialMaxAllowedValidators: u16 = 128; - pub const SubtensorInitialTempo: u16 = 99; + pub const SubtensorInitialTempo: u16 = INITIAL_SUBNET_TEMPO; pub const SubtensorInitialDifficulty: u64 = 10_000_000; pub const SubtensorInitialAdjustmentInterval: u16 = 100; pub const SubtensorInitialAdjustmentAlpha: u64 = 0; // no weight to previous value. @@ -858,7 +908,10 @@ 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 = 5_898; // 9% + pub const SubtensorInitialMinDelegateTake: u16 = 0; // Allow 0% delegate take + pub const SubtensorInitialDefaultChildKeyTake: u16 = 0; // Allow 0% childkey take + pub const SubtensorInitialMinChildKeyTake: u16 = 0; // 0 % + pub const SubtensorInitialMaxChildKeyTake: u16 = 11_796; // 18 % pub const SubtensorInitialWeightsVersionKey: u64 = 0; pub const SubtensorInitialMinDifficulty: u64 = 10_000_000; pub const SubtensorInitialMaxDifficulty: u64 = u64::MAX / 4; @@ -868,6 +921,7 @@ parameter_types! { pub const SubtensorInitialMaxBurn: u64 = 100_000_000_000; // 100 tao pub const SubtensorInitialTxRateLimit: u64 = 1000; pub const SubtensorInitialTxDelegateTakeRateLimit: u64 = 216000; // 30 days at 12 seconds per block + pub const SubtensorInitialTxChildKeyTakeRateLimit: u64 = INITIAL_CHILDKEY_TAKE_RATELIMIT; pub const SubtensorInitialRAORecycledForRegistration: u64 = 0; // 0 rao pub const SubtensorInitialSenateRequiredStakePercentage: u64 = 1; // 1 percent of total stake pub const SubtensorInitialNetworkImmunity: u64 = 7 * 7200; @@ -883,17 +937,21 @@ parameter_types! { pub const InitialAlphaLow: u16 = 45875; // Represents 0.7 as per the production default pub const InitialLiquidAlphaOn: bool = false; // Default value for LiquidAlphaOn pub const SubtensorInitialHotkeyEmissionTempo: u64 = 7200; // Drain every day. - pub const SubtensorInitialNetworkMaxStake: u64 = 500_000_000_000_000; // 500_000 TAO + pub const SubtensorInitialNetworkMaxStake: u64 = u64::MAX; // Maximum possible value for u64, this make the make stake infinity + pub const InitialColdkeySwapScheduleDuration: BlockNumber = 5 * 24 * 60 * 60 / 12; // 5 days + pub const InitialDissolveNetworkScheduleDuration: BlockNumber = 5 * 24 * 60 * 60 / 12; // 5 days + } impl pallet_subtensor::Config for Runtime { type RuntimeEvent = RuntimeEvent; + type RuntimeCall = RuntimeCall; type SudoRuntimeCall = RuntimeCall; type Currency = Balances; type CouncilOrigin = EnsureMajoritySenate; type SenateMembers = ManageSenateMembers; type TriumvirateInterface = TriumvirateVotes; - + type Scheduler = Scheduler; type InitialRho = SubtensorInitialRho; type InitialKappa = SubtensorInitialKappa; type InitialMaxAllowedUids = SubtensorInitialMaxAllowedUids; @@ -914,8 +972,10 @@ impl pallet_subtensor::Config for Runtime { type InitialMaxRegistrationsPerBlock = SubtensorInitialMaxRegistrationsPerBlock; type InitialPruningScore = SubtensorInitialPruningScore; type InitialMaxAllowedValidators = SubtensorInitialMaxAllowedValidators; - type InitialDefaultTake = SubtensorInitialDefaultTake; - type InitialMinTake = SubtensorInitialMinTake; + type InitialDefaultDelegateTake = SubtensorInitialDefaultTake; + type InitialDefaultChildKeyTake = SubtensorInitialDefaultChildKeyTake; + type InitialMinDelegateTake = SubtensorInitialMinDelegateTake; + type InitialMinChildKeyTake = SubtensorInitialMinChildKeyTake; type InitialWeightsVersionKey = SubtensorInitialWeightsVersionKey; type InitialMaxDifficulty = SubtensorInitialMaxDifficulty; type InitialMinDifficulty = SubtensorInitialMinDifficulty; @@ -925,6 +985,8 @@ impl pallet_subtensor::Config for Runtime { type InitialMinBurn = SubtensorInitialMinBurn; type InitialTxRateLimit = SubtensorInitialTxRateLimit; type InitialTxDelegateTakeRateLimit = SubtensorInitialTxDelegateTakeRateLimit; + type InitialTxChildKeyTakeRateLimit = SubtensorInitialTxChildKeyTakeRateLimit; + type InitialMaxChildKeyTake = SubtensorInitialMaxChildKeyTake; type InitialRAORecycledForRegistration = SubtensorInitialRAORecycledForRegistration; type InitialSenateRequiredStakePercentage = SubtensorInitialSenateRequiredStakePercentage; type InitialNetworkImmunityPeriod = SubtensorInitialNetworkImmunity; @@ -941,6 +1003,9 @@ impl pallet_subtensor::Config for Runtime { type LiquidAlphaOn = InitialLiquidAlphaOn; type InitialHotkeyEmissionTempo = SubtensorInitialHotkeyEmissionTempo; type InitialNetworkMaxStake = SubtensorInitialNetworkMaxStake; + type Preimages = Preimage; + type InitialColdkeySwapScheduleDuration = InitialColdkeySwapScheduleDuration; + type InitialDissolveNetworkScheduleDuration = InitialDissolveNetworkScheduleDuration; } use sp_runtime::BoundedVec; @@ -952,299 +1017,12 @@ impl pallet_admin_utils::AuraInterface> for AuraPalletIntrf } } -pub struct SubtensorInterface; - -impl - pallet_admin_utils::SubtensorInterface< - AccountId, - as frame_support::traits::Currency>::Balance, - RuntimeOrigin, - > for SubtensorInterface -{ - fn set_max_delegate_take(max_take: u16) { - SubtensorModule::set_max_delegate_take(max_take); - } - - fn set_min_delegate_take(max_take: u16) { - SubtensorModule::set_min_delegate_take(max_take); - } - - fn set_tx_rate_limit(rate_limit: u64) { - SubtensorModule::set_tx_rate_limit(rate_limit); - } - - fn set_tx_delegate_take_rate_limit(rate_limit: u64) { - SubtensorModule::set_tx_delegate_take_rate_limit(rate_limit); - } - - fn set_serving_rate_limit(netuid: u16, rate_limit: u64) { - SubtensorModule::set_serving_rate_limit(netuid, rate_limit); - } - - fn set_max_burn(netuid: u16, max_burn: u64) { - SubtensorModule::set_max_burn(netuid, max_burn); - } - - fn set_min_burn(netuid: u16, min_burn: u64) { - SubtensorModule::set_min_burn(netuid, min_burn); - } - - fn set_burn(netuid: u16, burn: u64) { - SubtensorModule::set_burn(netuid, burn); - } - - fn set_max_difficulty(netuid: u16, max_diff: u64) { - SubtensorModule::set_max_difficulty(netuid, max_diff); - } - - fn set_min_difficulty(netuid: u16, min_diff: u64) { - SubtensorModule::set_min_difficulty(netuid, min_diff); - } - - fn set_difficulty(netuid: u16, diff: u64) { - SubtensorModule::set_difficulty(netuid, diff); - } - - fn set_weights_rate_limit(netuid: u16, rate_limit: u64) { - SubtensorModule::set_weights_set_rate_limit(netuid, rate_limit); - } - - fn set_weights_version_key(netuid: u16, version: u64) { - SubtensorModule::set_weights_version_key(netuid, version); - } - - fn set_bonds_moving_average(netuid: u16, moving_average: u64) { - SubtensorModule::set_bonds_moving_average(netuid, moving_average); - } - - fn set_max_allowed_validators(netuid: u16, max_validators: u16) { - SubtensorModule::set_max_allowed_validators(netuid, max_validators); - } - - fn get_root_netuid() -> u16 { - SubtensorModule::get_root_netuid() - } - - fn if_subnet_exist(netuid: u16) -> bool { - SubtensorModule::if_subnet_exist(netuid) - } - - fn create_account_if_non_existent(coldkey: &AccountId, hotkey: &AccountId) { - SubtensorModule::create_account_if_non_existent(coldkey, hotkey) - } - - fn coldkey_owns_hotkey(coldkey: &AccountId, hotkey: &AccountId) -> bool { - SubtensorModule::coldkey_owns_hotkey(coldkey, hotkey) - } - - fn increase_stake_on_coldkey_hotkey_account( - coldkey: &AccountId, - hotkey: &AccountId, - increment: u64, - ) { - SubtensorModule::increase_stake_on_coldkey_hotkey_account(coldkey, hotkey, increment); - } - - fn add_balance_to_coldkey_account(coldkey: &AccountId, amount: Balance) { - SubtensorModule::add_balance_to_coldkey_account(coldkey, amount); - } - - fn get_current_block_as_u64() -> u64 { - SubtensorModule::get_current_block_as_u64() - } - - fn get_subnetwork_n(netuid: u16) -> u16 { - SubtensorModule::get_subnetwork_n(netuid) - } - - fn get_max_allowed_uids(netuid: u16) -> u16 { - SubtensorModule::get_max_allowed_uids(netuid) - } - - fn append_neuron(netuid: u16, new_hotkey: &AccountId, block_number: u64) { - SubtensorModule::append_neuron(netuid, new_hotkey, block_number) - } - - fn get_neuron_to_prune(netuid: u16) -> u16 { - SubtensorModule::get_neuron_to_prune(netuid) - } - - fn replace_neuron(netuid: u16, uid_to_replace: u16, new_hotkey: &AccountId, block_number: u64) { - SubtensorModule::replace_neuron(netuid, uid_to_replace, new_hotkey, block_number); - } - - fn set_total_issuance(total_issuance: u64) { - SubtensorModule::set_total_issuance(total_issuance); - } - - fn set_network_immunity_period(net_immunity_period: u64) { - SubtensorModule::set_network_immunity_period(net_immunity_period); - } - - fn set_network_min_lock(net_min_lock: u64) { - SubtensorModule::set_network_min_lock(net_min_lock); - } - - fn set_subnet_limit(limit: u16) { - SubtensorModule::set_max_subnets(limit); - } - - fn set_lock_reduction_interval(interval: u64) { - SubtensorModule::set_lock_reduction_interval(interval); - } - - fn set_tempo(netuid: u16, tempo: u16) { - SubtensorModule::set_tempo(netuid, tempo); - } - - fn set_subnet_owner_cut(subnet_owner_cut: u16) { - SubtensorModule::set_subnet_owner_cut(subnet_owner_cut); - } - - fn set_network_rate_limit(limit: u64) { - SubtensorModule::set_network_rate_limit(limit); - } - - fn set_max_registrations_per_block(netuid: u16, max_registrations_per_block: u16) { - SubtensorModule::set_max_registrations_per_block(netuid, max_registrations_per_block); - } - - fn set_adjustment_alpha(netuid: u16, adjustment_alpha: u64) { - SubtensorModule::set_adjustment_alpha(netuid, adjustment_alpha); - } - - fn set_target_registrations_per_interval(netuid: u16, target_registrations_per_interval: u16) { - SubtensorModule::set_target_registrations_per_interval( - netuid, - target_registrations_per_interval, - ); - } - - fn set_network_pow_registration_allowed(netuid: u16, registration_allowed: bool) { - SubtensorModule::set_network_pow_registration_allowed(netuid, registration_allowed); - } - - fn set_network_registration_allowed(netuid: u16, registration_allowed: bool) { - SubtensorModule::set_network_registration_allowed(netuid, registration_allowed); - } - - fn set_activity_cutoff(netuid: u16, activity_cutoff: u16) { - SubtensorModule::set_activity_cutoff(netuid, activity_cutoff); - } - - fn ensure_subnet_owner_or_root(o: RuntimeOrigin, netuid: u16) -> Result<(), DispatchError> { - SubtensorModule::ensure_subnet_owner_or_root(o, netuid) - } - - fn set_rho(netuid: u16, rho: u16) { - SubtensorModule::set_rho(netuid, rho); - } - - fn set_kappa(netuid: u16, kappa: u16) { - SubtensorModule::set_kappa(netuid, kappa); - } - - fn set_max_allowed_uids(netuid: u16, max_allowed: u16) { - SubtensorModule::set_max_allowed_uids(netuid, max_allowed); - } - - fn set_min_allowed_weights(netuid: u16, min_allowed_weights: u16) { - SubtensorModule::set_min_allowed_weights(netuid, min_allowed_weights); - } - - fn set_immunity_period(netuid: u16, immunity_period: u16) { - SubtensorModule::set_immunity_period(netuid, immunity_period); - } - - fn set_max_weight_limit(netuid: u16, max_weight_limit: u16) { - SubtensorModule::set_max_weight_limit(netuid, max_weight_limit); - } - - fn set_scaling_law_power(netuid: u16, scaling_law_power: u16) { - SubtensorModule::set_scaling_law_power(netuid, scaling_law_power); - } - - fn set_validator_prune_len(netuid: u16, validator_prune_len: u64) { - SubtensorModule::set_validator_prune_len(netuid, validator_prune_len); - } - - fn set_adjustment_interval(netuid: u16, adjustment_interval: u16) { - SubtensorModule::set_adjustment_interval(netuid, adjustment_interval); - } - - fn set_weights_set_rate_limit(netuid: u16, weights_set_rate_limit: u64) { - SubtensorModule::set_weights_set_rate_limit(netuid, weights_set_rate_limit); - } - - fn set_rao_recycled(netuid: u16, rao_recycled: u64) { - SubtensorModule::set_rao_recycled(netuid, rao_recycled); - } - - fn is_hotkey_registered_on_network(netuid: u16, hotkey: &AccountId) -> bool { - SubtensorModule::is_hotkey_registered_on_network(netuid, hotkey) - } - - fn init_new_network(netuid: u16, tempo: u16) { - SubtensorModule::init_new_network(netuid, tempo); - } - - fn set_weights_min_stake(min_stake: u64) { - SubtensorModule::set_weights_min_stake(min_stake); - } - - fn clear_small_nominations() { - SubtensorModule::clear_small_nominations(); - } - - fn set_nominator_min_required_stake(min_stake: u64) { - SubtensorModule::set_nominator_min_required_stake(min_stake); - } - - fn get_nominator_min_required_stake() -> u64 { - SubtensorModule::get_nominator_min_required_stake() - } - - fn set_target_stakes_per_interval(target_stakes_per_interval: u64) { - SubtensorModule::set_target_stakes_per_interval(target_stakes_per_interval) - } - - fn set_commit_reveal_weights_interval(netuid: u16, interval: u64) { - SubtensorModule::set_commit_reveal_weights_interval(netuid, interval); - } - - fn set_commit_reveal_weights_enabled(netuid: u16, enabled: bool) { - SubtensorModule::set_commit_reveal_weights_enabled(netuid, enabled); - } - - fn set_liquid_alpha_enabled(netuid: u16, enabled: bool) { - SubtensorModule::set_liquid_alpha_enabled(netuid, enabled); - } - - fn set_hotkey_emission_tempo(emission_tempo: u64) { - SubtensorModule::set_hotkey_emission_tempo(emission_tempo); - } - - fn set_network_max_stake(netuid: u16, max_stake: u64) { - SubtensorModule::set_network_max_stake(netuid, max_stake); - } - - fn do_set_alpha_values( - origin: RuntimeOrigin, - netuid: u16, - alpha_low: u16, - alpha_high: u16, - ) -> Result<(), DispatchError> { - SubtensorModule::do_set_alpha_values(origin, netuid, alpha_low, alpha_high) - } -} - impl pallet_admin_utils::Config for Runtime { type RuntimeEvent = RuntimeEvent; type AuthorityId = AuraId; type MaxAuthorities = ConstU32<32>; type Aura = AuraPalletIntrf; type Balance = Balance; - type Subtensor = SubtensorInterface; type WeightInfo = pallet_admin_utils::weights::SubstrateWeight; } @@ -1267,12 +1045,12 @@ construct_runtime!( Sudo: pallet_sudo, Multisig: pallet_multisig, Preimage: pallet_preimage, - Scheduler: pallet_scheduler, Proxy: pallet_proxy, Registry: pallet_registry, Commitments: pallet_commitments, AdminUtils: pallet_admin_utils, SafeMode: pallet_safe_mode, + Scheduler: pallet_scheduler, } ); @@ -1297,7 +1075,10 @@ pub type SignedExtra = ( frame_metadata_hash_extension::CheckMetadataHash, ); -type Migrations = pallet_grandpa::migrations::MigrateV4ToV5; +type Migrations = + pallet_subtensor::migrations::migrate_init_total_issuance::initialise_total_issuance::Migration< + Runtime, + >; // Unchecked extrinsic type as expected by this runtime. pub type UncheckedExtrinsic = @@ -1538,10 +1319,7 @@ impl_runtime_apis! { use frame_system_benchmarking::Pallet as SystemBench; use baseline::Pallet as BaselineBench; - #[allow(non_local_definitions)] impl frame_system_benchmarking::Config for Runtime {} - - #[allow(non_local_definitions)] impl baseline::Config for Runtime {} use frame_support::traits::WhitelistedStorageKeys; diff --git a/runtime/tests/pallet_proxy.rs b/runtime/tests/pallet_proxy.rs index 796dfc471..eea250938 100644 --- a/runtime/tests/pallet_proxy.rs +++ b/runtime/tests/pallet_proxy.rs @@ -200,3 +200,30 @@ fn test_proxy_pallet() { } } } + +#[test] +fn test_non_transfer_cannot_transfer() { + new_test_ext().execute_with(|| { + assert_ok!(Proxy::add_proxy( + RuntimeOrigin::signed(AccountId::from(ACCOUNT)), + AccountId::from(DELEGATE).into(), + ProxyType::NonTransfer, + 0 + )); + + let call = call_transfer(); + assert_ok!(Proxy::proxy( + RuntimeOrigin::signed(AccountId::from(DELEGATE)), + AccountId::from(ACCOUNT).into(), + None, + Box::new(call.clone()), + )); + + System::assert_last_event( + pallet_proxy::Event::ProxyExecuted { + result: Err(SystemError::CallFiltered.into()), + } + .into(), + ); + }); +} diff --git a/scripts/benchmark_all.sh b/scripts/benchmark_all.sh new file mode 100755 index 000000000..580e5425e --- /dev/null +++ b/scripts/benchmark_all.sh @@ -0,0 +1,24 @@ +#!/bin/sh +set -ex + +# List of pallets you want to benchmark +pallets=("pallet_subtensor" "pallet_collective" "pallet_commitments" "pallet_registry" "pallet_admin_utils") + +# Chain spec and output directory +chain_spec="finney" # or your specific chain spec + +for pallet in "${pallets[@]}" +do + echo "Benchmarking $pallet..." + cargo run --profile=production --features=runtime-benchmarks,try-runtime --bin node-subtensor -- benchmark pallet \ + --chain $chain_spec \ + --wasm-execution=compiled \ + --pallet $pallet \ + --extrinsic '*' \ + --steps 50 \ + --repeat 5 \ + --output "pallets/$pallet/src/weights.rs" \ + --template ./.maintain/frame-weight-template.hbs # Adjust this path to your template file +done + +echo "All pallets have been benchmarked and weights updated." diff --git a/scripts/localnet.sh b/scripts/localnet.sh index 65ca5c0a9..850a314d8 100755 --- a/scripts/localnet.sh +++ b/scripts/localnet.sh @@ -1,5 +1,14 @@ #!/bin/bash +# Check if `--no-purge` passed as a parameter +NO_PURGE=0 +for arg in "$@"; do + if [ "$arg" = "--no-purge" ]; then + NO_PURGE=1 + break + fi +done + # Determine the directory this script resides in. This allows invoking it from any location. SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" &>/dev/null && pwd)" @@ -39,7 +48,7 @@ fi if [[ $BUILD_BINARY == "1" ]]; then echo "*** Building substrate binary..." - cargo build --release --features "$FEATURES" --manifest-path "$BASE_DIR/Cargo.toml" + cargo build --workspace --profile=release --features "$FEATURES" --manifest-path "$BASE_DIR/Cargo.toml" echo "*** Binary compiled" fi @@ -47,10 +56,14 @@ echo "*** Building chainspec..." "$BASE_DIR/target/release/node-subtensor" build-spec --disable-default-bootnode --raw --chain $CHAIN >$FULL_PATH echo "*** Chainspec built and output to file" -echo "*** Purging previous state..." -"$BASE_DIR/target/release/node-subtensor" purge-chain -y --base-path /tmp/bob --chain="$FULL_PATH" >/dev/null 2>&1 -"$BASE_DIR/target/release/node-subtensor" purge-chain -y --base-path /tmp/alice --chain="$FULL_PATH" >/dev/null 2>&1 -echo "*** Previous chainstate purged" +if [ $NO_PURGE -eq 1 ]; then + echo "*** Purging previous state skipped..." +else + echo "*** Purging previous state..." + "$BASE_DIR/target/release/node-subtensor" purge-chain -y --base-path /tmp/bob --chain="$FULL_PATH" >/dev/null 2>&1 + "$BASE_DIR/target/release/node-subtensor" purge-chain -y --base-path /tmp/alice --chain="$FULL_PATH" >/dev/null 2>&1 + echo "*** Previous chainstate purged" +fi echo "*** Starting localnet nodes..." alice_start=( diff --git a/scripts/test_specific.sh b/scripts/test_specific.sh index 018872d33..85f3ebe30 100755 --- a/scripts/test_specific.sh +++ b/scripts/test_specific.sh @@ -1,6 +1,4 @@ pallet="${3:-pallet-subtensor}" features="${4:-pow-faucet}" -# RUST_LOG="pallet_subtensor=info" cargo test --release --features=$features -p $pallet --test $1 -- $2 --nocapture --exact - -RUST_LOG=INFO cargo test --release --features=$features -p $pallet --test $1 -- $2 --nocapture --exact \ No newline at end of file +SKIP_WASM_BUILD=1 RUST_LOG=DEBUG cargo test --release --features=$features -p $pallet --test $1 -- $2 --nocapture --exact \ No newline at end of file diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 000000000..8b1378917 --- /dev/null +++ b/src/lib.rs @@ -0,0 +1 @@ + diff --git a/support/linting/Cargo.toml b/support/linting/Cargo.toml new file mode 100644 index 000000000..1e37d8163 --- /dev/null +++ b/support/linting/Cargo.toml @@ -0,0 +1,12 @@ +[package] +name = "subtensor-linting" +version = "0.1.0" +edition = "2021" + +[dependencies] +syn.workspace = true +quote.workspace = true +proc-macro2.workspace = true + +[lints] +workspace = true diff --git a/support/linting/src/lib.rs b/support/linting/src/lib.rs new file mode 100644 index 000000000..d02a70a2b --- /dev/null +++ b/support/linting/src/lib.rs @@ -0,0 +1,6 @@ +pub mod lint; +pub use lint::*; + +mod require_freeze_struct; + +pub use require_freeze_struct::RequireFreezeStruct; diff --git a/support/linting/src/lint.rs b/support/linting/src/lint.rs new file mode 100644 index 000000000..3c099d40c --- /dev/null +++ b/support/linting/src/lint.rs @@ -0,0 +1,13 @@ +use syn::File; + +pub type Result = core::result::Result<(), Vec>; + +/// A trait that defines custom lints that can be run within our workspace. +/// +/// Each lint is run in parallel on all Rust source files in the workspace. Within a lint you +/// can issue an error the same way you would in a proc macro, and otherwise return `Ok(())` if +/// there are no errors. +pub trait Lint: Send + Sync { + /// Lints the given Rust source file, returning a compile error if any issues are found. + fn lint(source: &File) -> Result; +} diff --git a/support/linting/src/require_freeze_struct.rs b/support/linting/src/require_freeze_struct.rs new file mode 100644 index 000000000..8f02e2697 --- /dev/null +++ b/support/linting/src/require_freeze_struct.rs @@ -0,0 +1,184 @@ +use super::*; +use syn::{ + parse_quote, punctuated::Punctuated, visit::Visit, Attribute, File, ItemStruct, Meta, MetaList, + Path, Token, +}; + +pub struct RequireFreezeStruct; + +impl Lint for RequireFreezeStruct { + fn lint(source: &File) -> Result { + let mut visitor = EncodeDecodeVisitor::default(); + + visitor.visit_file(source); + + if !visitor.errors.is_empty() { + return Err(visitor.errors); + } + + Ok(()) + } +} + +#[derive(Default)] +struct EncodeDecodeVisitor { + errors: Vec, +} + +impl<'ast> Visit<'ast> for EncodeDecodeVisitor { + fn visit_item_struct(&mut self, node: &'ast ItemStruct) { + let has_encode_decode = node.attrs.iter().any(is_derive_encode_or_decode); + let has_freeze_struct = node.attrs.iter().any(is_freeze_struct); + + if has_encode_decode && !has_freeze_struct { + self.errors.push(syn::Error::new( + node.ident.span(), + "Struct with Encode/Decode derive must also have #[freeze_struct(..)] attribute.", + )); + } + + syn::visit::visit_item_struct(self, node); + } +} + +fn is_freeze_struct(attr: &Attribute) -> bool { + if let Meta::List(meta_list) = &attr.meta { + let Some(seg) = meta_list.path.segments.last() else { + return false; + }; + if seg.ident == "freeze_struct" && !meta_list.tokens.is_empty() { + return true; + } + } + false +} + +fn is_derive_encode_or_decode(attr: &Attribute) -> bool { + if let Meta::List(MetaList { path, tokens, .. }) = &attr.meta { + if path.is_ident("derive") { + let nested: Punctuated = parse_quote!(#tokens); + return nested.iter().any(|nested| { + nested.segments.iter().any(|seg| seg.ident == "Encode") + || nested.segments.iter().any(|seg| seg.ident == "Decode") + }); + } + } + false +} + +#[cfg(test)] +mod tests { + use super::*; + + fn lint_struct(input: &str) -> Result { + let item_struct: ItemStruct = syn::parse_str(input).expect("should only use on a struct"); + let mut visitor = EncodeDecodeVisitor::default(); + visitor.visit_item_struct(&item_struct); + if !visitor.errors.is_empty() { + return Err(visitor.errors); + } + Ok(()) + } + + #[test] + fn test_no_attributes() { + let input = r#" + pub struct Test { + field: u32, + } + "#; + assert!(lint_struct(input).is_ok()); + } + + #[test] + fn test_freeze_struct_only() { + let input = r#" + #[freeze_struct("12345")] + pub struct Test { + field: u32, + } + "#; + assert!(lint_struct(input).is_ok()); + } + + #[test] + fn test_encode_only() { + let input = r#" + #[derive(Encode)] + pub struct Test { + field: u32, + } + "#; + assert!(lint_struct(input).is_err()); + } + + #[test] + fn test_decode_only() { + let input = r#" + #[derive(Decode)] + pub struct Test { + field: u32, + } + "#; + assert!(lint_struct(input).is_err()); + } + + #[test] + fn test_encode_and_freeze_struct() { + let input = r#" + #[freeze_struct("12345")] + #[derive(Encode)] + pub struct Test { + field: u32, + } + "#; + assert!(lint_struct(input).is_ok()); + } + + #[test] + fn test_decode_and_freeze_struct() { + let input = r#" + #[freeze_struct("12345")] + #[derive(Decode)] + pub struct Test { + field: u32, + } + "#; + assert!(lint_struct(input).is_ok()); + } + + #[test] + fn test_encode_decode_without_freeze_struct() { + let input = r#" + #[derive(Encode, Decode)] + pub struct Test { + field: u32, + } + "#; + assert!(lint_struct(input).is_err()); + } + + #[test] + fn test_encode_decode_with_freeze_struct() { + let input = r#" + #[freeze_struct("12345")] + #[derive(Encode, Decode)] + pub struct Test { + field: u32, + } + "#; + assert!(lint_struct(input).is_ok()); + } + + #[test] + fn test_temporary_freeze_struct() { + let input = r#" + #[freeze_struct] + #[derive(Encode, Decode)] + pub struct Test { + field: u32, + } + "#; + assert!(lint_struct(input).is_err()); + } +} diff --git a/support/macros/Cargo.toml b/support/macros/Cargo.toml index 10a15ba0d..b5a5febad 100644 --- a/support/macros/Cargo.toml +++ b/support/macros/Cargo.toml @@ -12,9 +12,9 @@ homepage = "https://bittensor.com/" proc-macro = true [dependencies] -syn = { version = "2", features = ["full", "visit-mut", "extra-traits"] } -proc-macro2 = "1" -quote = "1" +syn.workspace = true +proc-macro2.workspace = true +quote.workspace = true ahash = "0.8" [lints]