From 23d2cce2dce3b6febfc31e456ed5e8369f1d90b8 Mon Sep 17 00:00:00 2001
From: Pawel Polewicz
Date: Sun, 8 Mar 2026 13:06:06 +0000
Subject: [PATCH 01/16] Add /fix and /ship Claude Code skills for automated
lint, test, and ship workflow
Co-Authored-By: Claude Opus 4.6
---
.claude/skills/fix.md | 45 ++++++++++++++++++++
.claude/skills/ship.md | 94 ++++++++++++++++++++++++++++++++++++++++++
2 files changed, 139 insertions(+)
create mode 100644 .claude/skills/fix.md
create mode 100644 .claude/skills/ship.md
diff --git a/.claude/skills/fix.md b/.claude/skills/fix.md
new file mode 100644
index 0000000000..15d8adebf9
--- /dev/null
+++ b/.claude/skills/fix.md
@@ -0,0 +1,45 @@
+---
+name: fix
+description: Commit changes, run Rust fix tools, run tests, and amend with any fixes
+---
+
+# Fix Skill
+
+Commit current changes with a descriptive message, then run Rust fix tools one by one, amending the commit after each tool if it produced changes, then run unit tests and fix any failures.
+
+## Steps
+
+1. **Initial commit**: Stage all changes and create a commit with a descriptive message summarizing the changes (use `git add -A && git commit -m ""`). If there are no changes to commit, create no commit but still proceed with the fix tools below.
+
+2. **Run each fix tool in order**. After EACH tool, check `git status --porcelain` for changes. If there are changes, stage them and amend the commit (`git add -A && git commit --amend --no-edit`).
+
+ The tools to run in order:
+
+ a. `cargo check --workspace`
+ b. `cargo clippy --fix --workspace --all-features --all-targets --allow-dirty`
+ c. `cargo fix --workspace --all-features --all-targets --allow-dirty`
+ d. `cargo fmt --all`
+
+3. **Run unit tests in a Sonnet subagent**: Launch a Task subagent (subagent_type: `general-purpose`, model: `sonnet`) that runs:
+ ```
+ cargo test -p pallet-subtensor --lib
+ ```
+ The subagent must:
+ - Run the test command and capture full output.
+ - If all tests pass, report success and return.
+ - If any tests fail, analyze the failures: read the failing test code AND the source code it tests, determine the root cause, apply fixes using Edit tools, and re-run the tests to confirm the fix works.
+ - After fixing, if there are further failures, repeat (up to 3 fix-and-retest cycles).
+ - Return a summary of: which tests failed, what was fixed, and whether all tests pass now.
+
+4. **Amend commit with test fixes**: After the subagent returns, if any code changes were made (check `git status --porcelain`), stage and amend the commit (`git add -A && git commit --amend --no-edit`). Then re-run the fix tools from step 2 (since code changes from test fixes may need formatting/clippy cleanup), amending after each if there are changes.
+
+5. **Final output**: Show `git log --oneline -1` so the user can see the resulting commit.
+
+## Important
+
+- Use `--allow-dirty` flags on clippy --fix and cargo fix since the working tree may have unstaged changes between steps.
+- If a fix tool fails (step 2/4), stop and report the error to the user rather than continuing.
+- Do NOT run `scripts/fix_rust.sh` itself — run the individual commands listed above instead.
+- Do NOT skip any step. Run all four fix tools even if earlier ones produced no changes.
+- The test subagent must fix source code to make tests pass, NOT modify tests to make them pass (unless the test itself is clearly wrong).
+- If the test subagent cannot fix all failures after 3 cycles, it must return the remaining failures so the main agent can report them to the user.
diff --git a/.claude/skills/ship.md b/.claude/skills/ship.md
new file mode 100644
index 0000000000..58ca7e0e32
--- /dev/null
+++ b/.claude/skills/ship.md
@@ -0,0 +1,94 @@
+---
+name: ship
+description: Ship the current branch: fix, push, create PR, watch CI, fix failures, code review
+---
+
+# Ship Skill
+
+Ship the current branch: fix, push, create PR if needed, watch CI, fix failures, and perform code review.
+
+## Phase 1: Fix and Push
+
+1. **Run `/fix`** — invoke the fix skill to commit, lint, and format.
+2. **Push the branch** to origin: `git push -u origin HEAD`.
+3. **Create a PR if none exists**:
+ - Check: `gh pr view --json number 2>/dev/null` — if it fails, no PR exists yet.
+ - If no PR exists, create one:
+ - Use `git log main..HEAD --oneline` to understand all commits on the branch.
+ - Read the changed files with `git diff main...HEAD --stat` to understand scope.
+ - Create the PR with `gh pr create --title "" --body "" --label "skip-cargo-audit"`.
+ - The description must include: a **Summary** section (bullet points of what changed and why), a **Changes** section (key files/modules affected), and a **Test plan** section.
+ - If a PR already exists, just note its number/URL.
+
+## Phase 2: Watch CI and Fix Failures
+
+4. **Poll CI status** in a loop:
+ - Run: `gh pr checks --json name,state,conclusion,link --watch --fail-fast 2>/dev/null || gh pr checks`
+ - If `--watch` is not available, poll manually every 90 seconds using `gh pr checks --json name,state,conclusion,link` until all checks have completed (no checks with state "pending" or conclusion "").
+ - **Ignore these known-flaky/irrelevant checks** — treat them as passing even if they fail:
+ - `validate-benchmarks` (benchmark CI — not relevant)
+ - Any `Contract E2E Tests` check that failed only due to a timeout (look for timeout in the failure link/logs)
+ - `cargo-audit` (we already added the skip label)
+ - Also ignore any checks related to `check-spec-version` and `e2e` tests — these are environment-dependent and not fixable from code.
+
+5. **If there are real CI failures** (failures NOT in the ignore list above):
+ - For EACH distinct failing check, launch a **separate Task subagent** (subagent_type: `general-purpose`, model: `sonnet`) in parallel. Each subagent must:
+ - Fetch the failed check's logs: use `gh run view --log-failed` or the check link to get failure details.
+ - Investigate the root cause by reading relevant source files.
+ - Return a **fix plan**: a description of what needs to change and in which files, with specific code snippets showing the fix.
+ - **Wait for all subagents** to return their fix plans.
+
+6. **Aggregate and apply fixes**:
+ - Review all returned fix plans for conflicts or overlaps.
+ - Apply the fixes using Edit/Write tools.
+ - Run `/fix` again (invoke the fix skill) to commit, lint, and format the fixes.
+ - Push: `git push`.
+
+7. **Re-check CI**: Go back to step 4 and poll again. Repeat the fix cycle up to **3 times**. If CI still fails after 3 rounds, report the remaining failures to the user and stop.
+
+## Phase 3: Code Review
+
+8. **Once CI is green** (or only ignored checks are failing), perform a thorough code review.
+
+9. **Launch a single Opus subagent** (subagent_type: `general-purpose`, model: `opus`) for the review:
+ - It must get the full PR diff: `git diff main...HEAD`.
+ - It must read every changed file in full.
+ - It must produce a numbered list of **issues** found, where each issue has:
+ - A unique sequential ID (e.g., `R-1`, `R-2`, ...).
+ - **Severity**: critical / major / minor / nit.
+ - **File and line(s)** affected.
+ - **Description** of the problem.
+ - The review must check for: correctness, safety (no panics, no unchecked arithmetic, no indexing), edge cases, naming, documentation gaps, test coverage, and adherence to Substrate/Rust best practices.
+ - Return the full list of issues.
+
+10. **For each issue**, launch TWO subagents **in parallel**:
+ - **Fix designer** (subagent_type: `general-purpose`, model: `sonnet`): Given the issue description and relevant code context, design a concrete proposed fix with exact code changes (old code -> new code). Return the fix as a structured plan.
+ - **Fix reviewer** (subagent_type: `general-purpose`, model: `opus`): Given the issue description, the relevant code context, and the proposed fix (once the fix designer returns — so the reviewer runs AFTER the designer, but reviewers for different issues run in parallel with each other). The reviewer must check:
+ - Does the fix actually solve the issue?
+ - Does it introduce new problems?
+ - Is it the simplest correct fix?
+ - Return: approved / rejected with reasoning.
+
+ Implementation note: For each issue, first launch the fix designer. Once the fix designer for that issue returns, launch the fix reviewer for that issue. But all issues should be processed in parallel — i.e., launch all fix designers at once, then as each designer returns, launch its corresponding reviewer. You may batch reviewers if designers finish close together.
+
+11. **Report to user**: Present a formatted summary:
+ ```
+ ## Code Review Results
+
+ ### R-1: [severity]
+ **File**: path/to/file.rs:42
+ **Issue**:
+ **Proposed fix**:
+ **Review**: Approved / Rejected —
+
+ ### R-2: ...
+ ```
+ Ask the user which fixes to apply (all approved ones, specific ones by ID, or none).
+
+## Important Rules
+
+- Never force-push. Always use regular `git push`.
+- All CI polling must have a maximum total wall-clock timeout of 45 minutes. If CI hasn't finished by then, report current status and stop waiting.
+- When fetching CI logs, if `gh run view` output is very long, focus on the failed step output only.
+- Do NOT apply code review fixes automatically — always present them for user approval first.
+- Use HEREDOC syntax for PR body and commit messages to preserve formatting.
From 093a7fa91b61043a5377a0db133281a00f360e7c Mon Sep 17 00:00:00 2001
From: Pawel Polewicz
Date: Thu, 12 Feb 2026 23:39:49 +0000
Subject: [PATCH 02/16] Fix skills location
---
.claude/skills/{fix.md => fix/SKILL.md} | 0
.claude/skills/{ship.md => ship/SKILL.md} | 0
2 files changed, 0 insertions(+), 0 deletions(-)
rename .claude/skills/{fix.md => fix/SKILL.md} (100%)
rename .claude/skills/{ship.md => ship/SKILL.md} (100%)
diff --git a/.claude/skills/fix.md b/.claude/skills/fix/SKILL.md
similarity index 100%
rename from .claude/skills/fix.md
rename to .claude/skills/fix/SKILL.md
diff --git a/.claude/skills/ship.md b/.claude/skills/ship/SKILL.md
similarity index 100%
rename from .claude/skills/ship.md
rename to .claude/skills/ship/SKILL.md
From 90fb2ed78c04703f32010160d3dbf2e2e51bd243 Mon Sep 17 00:00:00 2001
From: Pawel Polewicz
Date: Wed, 25 Mar 2026 20:03:41 +0000
Subject: [PATCH 03/16] Distill fix/ship skill descriptions and opening
guidance
---
.claude/skills/fix/SKILL.md | 4 ++--
.claude/skills/ship/SKILL.md | 4 ++--
2 files changed, 4 insertions(+), 4 deletions(-)
diff --git a/.claude/skills/fix/SKILL.md b/.claude/skills/fix/SKILL.md
index 15d8adebf9..4fac13c956 100644
--- a/.claude/skills/fix/SKILL.md
+++ b/.claude/skills/fix/SKILL.md
@@ -1,11 +1,11 @@
---
name: fix
-description: Commit changes, run Rust fix tools, run tests, and amend with any fixes
+description: Commit current changes, run Rust autofix/lint/format, run pallet-subtensor tests, amend with any fixes.
---
# Fix Skill
-Commit current changes with a descriptive message, then run Rust fix tools one by one, amending the commit after each tool if it produced changes, then run unit tests and fix any failures.
+Create or reuse one commit, run the Rust fix pipeline in order, run unit tests, and fold all resulting changes into that same commit.
## Steps
diff --git a/.claude/skills/ship/SKILL.md b/.claude/skills/ship/SKILL.md
index 58ca7e0e32..b2b70de7f6 100644
--- a/.claude/skills/ship/SKILL.md
+++ b/.claude/skills/ship/SKILL.md
@@ -1,11 +1,11 @@
---
name: ship
-description: Ship the current branch: fix, push, create PR, watch CI, fix failures, code review
+description: Ship current branch end-to-end: run /fix, push, open/update PR, triage CI failures, then deliver review findings for approval.
---
# Ship Skill
-Ship the current branch: fix, push, create PR if needed, watch CI, fix failures, and perform code review.
+Ship the branch through CI and review without force-pushes, and never apply review fixes without explicit user approval.
## Phase 1: Fix and Push
From 148287c6fb4ea9a1cab412ec3efbc4214d99e71c Mon Sep 17 00:00:00 2001
From: Pawel Polewicz
Date: Wed, 25 Mar 2026 20:58:51 +0000
Subject: [PATCH 04/16] hand-tweak /fix and /format
---
.claude/skills/fix/SKILL.md | 50 +++++++++++++---------------------
.claude/skills/format/SKILL.md | 25 +++++++++++++++++
2 files changed, 44 insertions(+), 31 deletions(-)
create mode 100644 .claude/skills/format/SKILL.md
diff --git a/.claude/skills/fix/SKILL.md b/.claude/skills/fix/SKILL.md
index 4fac13c956..8f23de95aa 100644
--- a/.claude/skills/fix/SKILL.md
+++ b/.claude/skills/fix/SKILL.md
@@ -9,37 +9,25 @@ Create or reuse one commit, run the Rust fix pipeline in order, run unit tests,
## Steps
-1. **Initial commit**: Stage all changes and create a commit with a descriptive message summarizing the changes (use `git add -A && git commit -m ""`). If there are no changes to commit, create no commit but still proceed with the fix tools below.
-
-2. **Run each fix tool in order**. After EACH tool, check `git status --porcelain` for changes. If there are changes, stage them and amend the commit (`git add -A && git commit --amend --no-edit`).
-
- The tools to run in order:
-
- a. `cargo check --workspace`
- b. `cargo clippy --fix --workspace --all-features --all-targets --allow-dirty`
- c. `cargo fix --workspace --all-features --all-targets --allow-dirty`
- d. `cargo fmt --all`
-
-3. **Run unit tests in a Sonnet subagent**: Launch a Task subagent (subagent_type: `general-purpose`, model: `sonnet`) that runs:
- ```
- cargo test -p pallet-subtensor --lib
- ```
- The subagent must:
- - Run the test command and capture full output.
- - If all tests pass, report success and return.
- - If any tests fail, analyze the failures: read the failing test code AND the source code it tests, determine the root cause, apply fixes using Edit tools, and re-run the tests to confirm the fix works.
- - After fixing, if there are further failures, repeat (up to 3 fix-and-retest cycles).
- - Return a summary of: which tests failed, what was fixed, and whether all tests pass now.
-
-4. **Amend commit with test fixes**: After the subagent returns, if any code changes were made (check `git status --porcelain`), stage and amend the commit (`git add -A && git commit --amend --no-edit`). Then re-run the fix tools from step 2 (since code changes from test fixes may need formatting/clippy cleanup), amending after each if there are changes.
-
-5. **Final output**: Show `git log --oneline -1` so the user can see the resulting commit.
+1. Run /format
+2. In a Sonnet subagent run:
+ - `cargo test -p pallet-subtensor --lib` and capture full output
+ - If any tests fail, analyze the failures
+ - Read the failing test code AND the source code it tests
+ - Determine the root cause
+ - Apply fixes using Edit tools
+ - Re-run the tests to confirm the fix works
+ - After fixing, if there are further failures, repeat (up to 3 fix-and-retest cycles)
+ - Summarize:
+ - Which tests failed, if any
+ - What was fixed and how
+ - Whether all tests pass now
+3. Amend commit with test fixes, if any, then /format
+4. Run `git show -s` for user to review
## Important
-- Use `--allow-dirty` flags on clippy --fix and cargo fix since the working tree may have unstaged changes between steps.
-- If a fix tool fails (step 2/4), stop and report the error to the user rather than continuing.
-- Do NOT run `scripts/fix_rust.sh` itself — run the individual commands listed above instead.
-- Do NOT skip any step. Run all four fix tools even if earlier ones produced no changes.
-- The test subagent must fix source code to make tests pass, NOT modify tests to make them pass (unless the test itself is clearly wrong).
-- If the test subagent cannot fix all failures after 3 cycles, it must return the remaining failures so the main agent can report them to the user.
+- Do NOT run `scripts/fix_rust.sh` — let /format take care of it
+- Do NOT skip any steps
+- The test subagent must fix source code to make tests pass, NOT modify tests to make them pass (unless the test itself is clearly wrong)
+- If the test subagent cannot fix all failures after 3 cycles, it must return the remaining failures so the main agent can report them to the user
diff --git a/.claude/skills/format/SKILL.md b/.claude/skills/format/SKILL.md
new file mode 100644
index 0000000000..363370d260
--- /dev/null
+++ b/.claude/skills/format/SKILL.md
@@ -0,0 +1,25 @@
+---
+name: format
+description: Commit current changes, run Rust autofix/lint/format, amend with any fixes.
+---
+
+# Format Skill
+
+Create or reuse one commit, run the Rust fix pipeline in order and fold all resulting changes into that same commit.
+
+## Steps
+
+1. Stage all changes and create a commit with a descriptive message summarizing the changes
+2. Do this:
+ a. Run `cargo check --workspace`
+ b. Run `cargo clippy --fix --workspace --all-features --all-targets --allow-dirty`
+ c. Run `cargo fix --workspace --all-features --all-targets --allow-dirty`
+ d. Run `cargo fmt --all`
+ e. Amend the commit with any changes
+3. Run `git show -s` for user to review
+
+## Important
+
+- If a fix tool fails in step 2, stop and report the error to the user rather than continuing
+- Do NOT run `scripts/fix_rust.sh` itself — run the individual commands listed above instead
+- Do NOT skip any steps
From b93f415885895ef61d3afdb2aac07f2ba47f8437 Mon Sep 17 00:00:00 2001
From: Pawel Polewicz
Date: Fri, 27 Mar 2026 17:15:30 +0000
Subject: [PATCH 05/16] hand-tweak /ship
---
.claude/skills/ship/SKILL.md | 62 +++++++++++++-----------------------
1 file changed, 22 insertions(+), 40 deletions(-)
diff --git a/.claude/skills/ship/SKILL.md b/.claude/skills/ship/SKILL.md
index b2b70de7f6..9e6400bd28 100644
--- a/.claude/skills/ship/SKILL.md
+++ b/.claude/skills/ship/SKILL.md
@@ -7,61 +7,43 @@ description: Ship current branch end-to-end: run /fix, push, open/update PR, tri
Ship the branch through CI and review without force-pushes, and never apply review fixes without explicit user approval.
-## Phase 1: Fix and Push
+Run the following skill in a subagent to prevent context pollution. Make the subagent return a short summary to the main agent.
-1. **Run `/fix`** — invoke the fix skill to commit, lint, and format.
-2. **Push the branch** to origin: `git push -u origin HEAD`.
-3. **Create a PR if none exists**:
- - Check: `gh pr view --json number 2>/dev/null` — if it fails, no PR exists yet.
- - If no PR exists, create one:
- - Use `git log main..HEAD --oneline` to understand all commits on the branch.
- - Read the changed files with `git diff main...HEAD --stat` to understand scope.
- - Create the PR with `gh pr create --title "" --body "" --label "skip-cargo-audit"`.
- - The description must include: a **Summary** section (bullet points of what changed and why), a **Changes** section (key files/modules affected), and a **Test plan** section.
- - If a PR already exists, just note its number/URL.
-
-## Phase 2: Watch CI and Fix Failures
-
-4. **Poll CI status** in a loop:
+1. Run `/fix`
+2. Push the branch to origin
+3. Create a PR with a comprehensive description if none exists yet
+ - Update the description if PR exists already
+4. Poll CI status in a loop:
- Run: `gh pr checks --json name,state,conclusion,link --watch --fail-fast 2>/dev/null || gh pr checks`
- If `--watch` is not available, poll manually every 90 seconds using `gh pr checks --json name,state,conclusion,link` until all checks have completed (no checks with state "pending" or conclusion "").
- **Ignore these known-flaky/irrelevant checks** — treat them as passing even if they fail:
- `validate-benchmarks` (benchmark CI — not relevant)
- Any `Contract E2E Tests` check that failed only due to a timeout (look for timeout in the failure link/logs)
- - `cargo-audit` (we already added the skip label)
- - Also ignore any checks related to `check-spec-version` and `e2e` tests — these are environment-dependent and not fixable from code.
-
+ - `cargo-audit`
5. **If there are real CI failures** (failures NOT in the ignore list above):
- For EACH distinct failing check, launch a **separate Task subagent** (subagent_type: `general-purpose`, model: `sonnet`) in parallel. Each subagent must:
- Fetch the failed check's logs: use `gh run view --log-failed` or the check link to get failure details.
- Investigate the root cause by reading relevant source files.
- Return a **fix plan**: a description of what needs to change and in which files, with specific code snippets showing the fix.
- **Wait for all subagents** to return their fix plans.
-
6. **Aggregate and apply fixes**:
- Review all returned fix plans for conflicts or overlaps.
- Apply the fixes using Edit/Write tools.
- - Run `/fix` again (invoke the fix skill) to commit, lint, and format the fixes.
- - Push: `git push`.
-
+ - Invoke the /fix skill
+ - `git push`
7. **Re-check CI**: Go back to step 4 and poll again. Repeat the fix cycle up to **3 times**. If CI still fails after 3 rounds, report the remaining failures to the user and stop.
-
-## Phase 3: Code Review
-
8. **Once CI is green** (or only ignored checks are failing), perform a thorough code review.
-
-9. **Launch a single Opus subagent** (subagent_type: `general-purpose`, model: `opus`) for the review:
- - It must get the full PR diff: `git diff main...HEAD`.
- - It must read every changed file in full.
- - It must produce a numbered list of **issues** found, where each issue has:
- - A unique sequential ID (e.g., `R-1`, `R-2`, ...).
- - **Severity**: critical / major / minor / nit.
- - **File and line(s)** affected.
- - **Description** of the problem.
- - The review must check for: correctness, safety (no panics, no unchecked arithmetic, no indexing), edge cases, naming, documentation gaps, test coverage, and adherence to Substrate/Rust best practices.
- - Return the full list of issues.
-
-10. **For each issue**, launch TWO subagents **in parallel**:
+ - **Launch a single Opus subagent** (subagent_type: `general-purpose`, model: `opus`) for the review:
+ - It must get the full PR diff: `git diff main...HEAD`.
+ - It must read every changed file in full.
+ - It must produce a numbered list of **issues** found, where each issue has:
+ - A unique sequential ID (e.g., `R-1`, `R-2`, ...).
+ - **Severity**: critical / major / minor / nit.
+ - **File and line(s)** affected.
+ - **Description** of the problem.
+ - The review must check for: correctness, safety (no panics, no unchecked arithmetic, no indexing), edge cases, naming, documentation gaps, test coverage, and adherence to Substrate/Rust best practices.
+ - Return the full list of issues.
+9. **For each issue**, launch TWO subagents **in parallel**:
- **Fix designer** (subagent_type: `general-purpose`, model: `sonnet`): Given the issue description and relevant code context, design a concrete proposed fix with exact code changes (old code -> new code). Return the fix as a structured plan.
- **Fix reviewer** (subagent_type: `general-purpose`, model: `opus`): Given the issue description, the relevant code context, and the proposed fix (once the fix designer returns — so the reviewer runs AFTER the designer, but reviewers for different issues run in parallel with each other). The reviewer must check:
- Does the fix actually solve the issue?
@@ -71,7 +53,7 @@ Ship the branch through CI and review without force-pushes, and never apply revi
Implementation note: For each issue, first launch the fix designer. Once the fix designer for that issue returns, launch the fix reviewer for that issue. But all issues should be processed in parallel — i.e., launch all fix designers at once, then as each designer returns, launch its corresponding reviewer. You may batch reviewers if designers finish close together.
-11. **Report to user**: Present a formatted summary:
+10. **Report to user**: Present a formatted summary:
```
## Code Review Results
@@ -89,6 +71,6 @@ Ship the branch through CI and review without force-pushes, and never apply revi
- Never force-push. Always use regular `git push`.
- All CI polling must have a maximum total wall-clock timeout of 45 minutes. If CI hasn't finished by then, report current status and stop waiting.
-- When fetching CI logs, if `gh run view` output is very long, focus on the failed step output only.
+- When fetching CI logs, use a subagent to isolate the relevant part. If `gh run view` output is very long, focus on the failed step output only.
- Do NOT apply code review fixes automatically — always present them for user approval first.
- Use HEREDOC syntax for PR body and commit messages to preserve formatting.
From de5a77d3582c0bf1f1f7a8f52966f63e845c0dd4 Mon Sep 17 00:00:00 2001
From: Pawel Polewicz
Date: Fri, 27 Mar 2026 19:18:08 +0000
Subject: [PATCH 06/16] Add CLAUDE.md
---
.claude/skills/fix/SKILL.md | 18 +++++++++---------
.claude/skills/format/SKILL.md | 2 +-
.claude/skills/ship/SKILL.md | 3 ++-
CLAUDE.md | 4 ++++
4 files changed, 16 insertions(+), 11 deletions(-)
create mode 100644 CLAUDE.md
diff --git a/.claude/skills/fix/SKILL.md b/.claude/skills/fix/SKILL.md
index 8f23de95aa..d097f3fbfe 100644
--- a/.claude/skills/fix/SKILL.md
+++ b/.claude/skills/fix/SKILL.md
@@ -10,18 +10,18 @@ Create or reuse one commit, run the Rust fix pipeline in order, run unit tests,
## Steps
1. Run /format
-2. In a Sonnet subagent run:
+2. In a subagent (subagent_type: `general-purpose`, model: `sonnet`) run:
- `cargo test -p pallet-subtensor --lib` and capture full output
- If any tests fail, analyze the failures
- - Read the failing test code AND the source code it tests
- - Determine the root cause
- - Apply fixes using Edit tools
- - Re-run the tests to confirm the fix works
- - After fixing, if there are further failures, repeat (up to 3 fix-and-retest cycles)
+ - Read the failing test code AND the source code it tests
+ - Determine the root cause
+ - Apply fixes using Edit tools
+ - Re-run the tests to confirm the fix works
+ - After fixing, if there are further failures, repeat (up to 3 fix-and-retest cycles)
- Summarize:
- - Which tests failed, if any
- - What was fixed and how
- - Whether all tests pass now
+ - Which tests failed, if any
+ - What was fixed and how
+ - Whether all tests pass now
3. Amend commit with test fixes, if any, then /format
4. Run `git show -s` for user to review
diff --git a/.claude/skills/format/SKILL.md b/.claude/skills/format/SKILL.md
index 363370d260..9a0424b28b 100644
--- a/.claude/skills/format/SKILL.md
+++ b/.claude/skills/format/SKILL.md
@@ -9,7 +9,7 @@ Create or reuse one commit, run the Rust fix pipeline in order and fold all resu
## Steps
-1. Stage all changes and create a commit with a descriptive message summarizing the changes
+1. Stage all changes and create a commit with a descriptive message summarizing the changes (unless there are none)
2. Do this:
a. Run `cargo check --workspace`
b. Run `cargo clippy --fix --workspace --all-features --all-targets --allow-dirty`
diff --git a/.claude/skills/ship/SKILL.md b/.claude/skills/ship/SKILL.md
index 9e6400bd28..163015b8b2 100644
--- a/.claude/skills/ship/SKILL.md
+++ b/.claude/skills/ship/SKILL.md
@@ -13,6 +13,7 @@ Run the following skill in a subagent to prevent context pollution. Make the sub
2. Push the branch to origin
3. Create a PR with a comprehensive description if none exists yet
- Update the description if PR exists already
+ - Add label `skip-cargo-audit` to the PR
4. Poll CI status in a loop:
- Run: `gh pr checks --json name,state,conclusion,link --watch --fail-fast 2>/dev/null || gh pr checks`
- If `--watch` is not available, poll manually every 90 seconds using `gh pr checks --json name,state,conclusion,link` until all checks have completed (no checks with state "pending" or conclusion "").
@@ -43,7 +44,7 @@ Run the following skill in a subagent to prevent context pollution. Make the sub
- **Description** of the problem.
- The review must check for: correctness, safety (no panics, no unchecked arithmetic, no indexing), edge cases, naming, documentation gaps, test coverage, and adherence to Substrate/Rust best practices.
- Return the full list of issues.
-9. **For each issue**, launch TWO subagents **in parallel**:
+9. **For each issue**, run fix designer then fix reviewer in sequence; run all issues concurrently with each other:
- **Fix designer** (subagent_type: `general-purpose`, model: `sonnet`): Given the issue description and relevant code context, design a concrete proposed fix with exact code changes (old code -> new code). Return the fix as a structured plan.
- **Fix reviewer** (subagent_type: `general-purpose`, model: `opus`): Given the issue description, the relevant code context, and the proposed fix (once the fix designer returns — so the reviewer runs AFTER the designer, but reviewers for different issues run in parallel with each other). The reviewer must check:
- Does the fix actually solve the issue?
diff --git a/CLAUDE.md b/CLAUDE.md
new file mode 100644
index 0000000000..2b6da8644e
--- /dev/null
+++ b/CLAUDE.md
@@ -0,0 +1,4 @@
+- never use slice indexing like `arr[n..]` or `arr[i]`; use `.get(n..)`, `.get(i)` etc. instead to avoid panics (clippy::indexing_slicing)
+- never use `*`, `+`, `-`, `/` for arithmetic; use `.saturating_mul()`, `.saturating_add()`, `.saturating_sub()`, `.saturating_div()` or checked variants instead (clippy::arithmetic_side_effects)
+- if you are creating a PR to `subtensor` add a `skip-cargo-audit` label
+- no `Co-Authored-By` in commits
From 331f4e449771425c83ef1b449eabb4d83e92c7a9 Mon Sep 17 00:00:00 2001
From: Pawel Polewicz
Date: Fri, 27 Mar 2026 19:30:18 +0000
Subject: [PATCH 07/16] tweak CLAUDE.md
---
CLAUDE.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/CLAUDE.md b/CLAUDE.md
index 2b6da8644e..2a40a8e0d8 100644
--- a/CLAUDE.md
+++ b/CLAUDE.md
@@ -1,4 +1,4 @@
- never use slice indexing like `arr[n..]` or `arr[i]`; use `.get(n..)`, `.get(i)` etc. instead to avoid panics (clippy::indexing_slicing)
- never use `*`, `+`, `-`, `/` for arithmetic; use `.saturating_mul()`, `.saturating_add()`, `.saturating_sub()`, `.saturating_div()` or checked variants instead (clippy::arithmetic_side_effects)
- if you are creating a PR to `subtensor` add a `skip-cargo-audit` label
-- no `Co-Authored-By` in commits
+- no `Co-Authored-By` in commits or attribution in PRs
From 34184a64665f63a98bc8a12639a0cebeba6dd087 Mon Sep 17 00:00:00 2001
From: konrad0960
Date: Mon, 10 Nov 2025 22:24:22 +0100
Subject: [PATCH 08/16] consensus modes
---
pallets/subtensor/src/epoch/run_epoch.rs | 98 +++-
pallets/subtensor/src/lib.rs | 41 +-
pallets/subtensor/src/tests/consensus_mode.rs | 421 ++++++++++++++++++
pallets/subtensor/src/tests/epoch.rs | 2 +
pallets/subtensor/src/tests/mod.rs | 1 +
pallets/subtensor/src/utils/misc.rs | 8 +
6 files changed, 559 insertions(+), 12 deletions(-)
create mode 100644 pallets/subtensor/src/tests/consensus_mode.rs
diff --git a/pallets/subtensor/src/epoch/run_epoch.rs b/pallets/subtensor/src/epoch/run_epoch.rs
index 793f6fbe4a..f8414864a7 100644
--- a/pallets/subtensor/src/epoch/run_epoch.rs
+++ b/pallets/subtensor/src/epoch/run_epoch.rs
@@ -1274,9 +1274,13 @@ impl Pallet {
.iter()
.any(|&c| c != I32F32::saturating_from_num(0))
{
- // Liquid Alpha is enabled, compute the liquid alphas matrix.
+ // Liquid Alpha is enabled, compute the appropriate consensus for liquid alpha based on mode
+ let consensus_for_liquid_alpha = Self::compute_consensus_for_liquid_alpha(netuid, consensus);
+ log::trace!("consensus_for_liquid_alpha: {:?}", &consensus_for_liquid_alpha);
+
+ // Compute the liquid alphas matrix.
let alphas: Vec> =
- Self::compute_liquid_alpha_values(netuid, weights, bonds, consensus);
+ Self::compute_liquid_alpha_values(netuid, weights, bonds, &consensus_for_liquid_alpha);
log::trace!("alphas: {:?}", &alphas);
// Compute the Exponential Moving Average (EMA) of bonds using the provided clamped alpha values.
@@ -1316,9 +1320,13 @@ impl Pallet {
.iter()
.any(|&c| c != I32F32::saturating_from_num(0))
{
- // Liquid Alpha is enabled, compute the liquid alphas matrix.
+ // Liquid Alpha is enabled, compute the appropriate consensus for liquid alpha based on mode
+ let consensus_for_liquid_alpha = Self::compute_consensus_for_liquid_alpha(netuid, consensus);
+ log::trace!("consensus_for_liquid_alpha: {:?}", &consensus_for_liquid_alpha);
+
+ // Compute the liquid alphas matrix.
let alphas: Vec> =
- Self::compute_liquid_alpha_values_sparse(netuid, weights, bonds, consensus);
+ Self::compute_liquid_alpha_values_sparse(netuid, weights, bonds, &consensus_for_liquid_alpha);
log::trace!("alphas: {:?}", &alphas);
// Compute the Exponential Moving Average (EMA) of bonds using the provided clamped alpha values.
@@ -1332,6 +1340,68 @@ impl Pallet {
}
}
+ /// Compute the consensus to use for liquid alpha calculation based on the configured mode
+ ///
+ /// # Args:
+ /// * `netuid` - The network ID.
+ /// * `current_consensus` - The current in-memory consensus values.
+ ///
+ /// # Returns:
+ /// A vector of consensus values to use for liquid alpha calculation
+ pub fn compute_consensus_for_liquid_alpha(
+ netuid: NetUid,
+ current_consensus: &[I32F32],
+ ) -> Vec {
+ let mode = Self::get_liquid_alpha_consensus_mode(netuid);
+ let bonds_penalty = Self::get_float_bonds_penalty(netuid);
+
+ match mode {
+ ConsensusMode::Current => {
+ // Use the in-memory consensus (current behavior)
+ current_consensus.to_vec()
+ }
+ ConsensusMode::Previous => {
+ // Use consensus from storage
+ let previous_consensus_u16 = Consensus::::get(netuid);
+ previous_consensus_u16
+ .iter()
+ .map(|&c| I32F32::saturating_from_num(c).safe_div(I32F32::saturating_from_num(u16::MAX)))
+ .collect()
+ }
+ ConsensusMode::Max => {
+ // Use element-wise max of current and previous
+ let previous_consensus_u16 = Consensus::::get(netuid);
+ let previous_consensus: Vec = previous_consensus_u16
+ .iter()
+ .map(|&c| I32F32::saturating_from_num(c).safe_div(I32F32::saturating_from_num(u16::MAX)))
+ .collect();
+
+ // For each position: max(current, previous)
+ // If previous doesn't exist at that index (network grew), treat previous as 0
+ current_consensus
+ .iter()
+ .enumerate()
+ .map(|(i, &curr)| {
+ let prev = previous_consensus.get(i).copied().unwrap_or(I32F32::from_num(0));
+ if curr > prev { curr } else { prev }
+ })
+ .collect()
+ }
+ ConsensusMode::Auto => {
+ // Auto mode: Previous if bond_penalty == 1, otherwise Current
+ if bonds_penalty == I32F32::from_num(1) {
+ let previous_consensus_u16 = Consensus::::get(netuid);
+ previous_consensus_u16
+ .iter()
+ .map(|&c| I32F32::saturating_from_num(c).safe_div(I32F32::saturating_from_num(u16::MAX)))
+ .collect()
+ } else {
+ current_consensus.to_vec()
+ }
+ }
+ }
+ }
+
/// Compute liquid alphas matrix
/// There is a separate alpha param for each validator-miner binding
///
@@ -1539,6 +1609,26 @@ impl Pallet {
Ok(())
}
+ pub fn do_set_liquid_alpha_consensus_mode(
+ origin: T::RuntimeOrigin,
+ netuid: NetUid,
+ mode: ConsensusMode,
+ ) -> Result<(), DispatchError> {
+ Self::ensure_subnet_owner_or_root(origin, netuid)?;
+
+ ensure!(
+ Self::get_liquid_alpha_enabled(netuid),
+ Error::::LiquidAlphaDisabled
+ );
+
+ Self::set_liquid_alpha_consensus_mode(netuid, mode.clone());
+
+ log::debug!(
+ "LiquidAlphaConsensusModeSet( netuid: {netuid:?}, mode: {mode:?} )",
+ );
+ Ok(())
+ }
+
pub fn do_reset_bonds(
netuid_index: NetUidStorageIndex,
account_id: &T::AccountId,
diff --git a/pallets/subtensor/src/lib.rs b/pallets/subtensor/src/lib.rs
index 54ccadc1a1..459403da32 100644
--- a/pallets/subtensor/src/lib.rs
+++ b/pallets/subtensor/src/lib.rs
@@ -79,17 +79,17 @@ pub const MAX_ROOT_CLAIM_THRESHOLD: u64 = 10_000_000;
#[frame_support::pallet]
#[allow(clippy::expect_used)]
pub mod pallet {
- use crate::RateLimitKey;
use crate::migrations;
use crate::subnets::leasing::{LeaseId, SubnetLeaseOf};
+ use crate::RateLimitKey;
use frame_support::Twox64Concat;
use frame_support::{
- BoundedVec,
dispatch::GetDispatchInfo,
pallet_prelude::{DispatchResult, StorageMap, ValueQuery, *},
traits::{
- OriginTrait, QueryPreimage, StorePreimage, UnfilteredDispatchable, tokens::fungible,
+ tokens::fungible, OriginTrait, QueryPreimage, StorePreimage, UnfilteredDispatchable,
},
+ BoundedVec,
};
use frame_system::pallet_prelude::*;
use pallet_drand::types::RoundNumber;
@@ -348,6 +348,19 @@ pub mod pallet {
},
}
+ /// Enum for consensus mode used in liquid alpha calculation
+ #[derive(Encode, Decode, Default, TypeInfo, Clone, PartialEq, Eq, Debug)]
+ pub enum ConsensusMode {
+ /// Use current in-memory consensus (current behavior)
+ Current,
+ /// Use previous consensus from storage
+ Previous,
+ /// Use max of current and previous consensus
+ Max,
+ /// Auto mode: Previous if bond_penalty == 1, otherwise Current
+ #[default]
+ Auto,
+ }
/// Default minimum root claim amount.
/// This is the minimum amount of root claim that can be made.
/// Any amount less than this will not be claimed.
@@ -951,6 +964,12 @@ pub mod pallet {
(45875, 58982)
}
+ #[pallet::type_value]
+ /// Default consensus mode for liquid alpha calculation
+ pub fn DefaultConsensusMode() -> ConsensusMode {
+ ConsensusMode::default()
+ }
+
/// Default value for coldkey swap announcement delay.
#[pallet::type_value]
pub fn DefaultColdkeySwapAnnouncementDelay() -> BlockNumberFor {
@@ -1918,6 +1937,12 @@ pub mod pallet {
pub type AlphaValues =
StorageMap<_, Identity, NetUid, (u16, u16), ValueQuery, DefaultAlphaValues>;
+ #[pallet::storage]
+ /// MAP ( netuid ) --> consensus mode for liquid alpha calculation
+ pub type LiquidAlphaConsensusMode =
+ StorageMap<_, Identity, NetUid, ConsensusMode, ValueQuery, DefaultConsensusMode>;
+
+ #[pallet::storage]
/// --- MAP ( netuid ) --> If subtoken trading enabled
#[pallet::storage]
pub type SubtokenEnabled =
@@ -1932,10 +1957,10 @@ pub mod pallet {
/// alpha = 0.1 means slow response, 10% weight to new values per epoch
pub fn DefaultVotingPowerEmaAlpha() -> u64 {
0_003_570_000_000_000_000 // 0.00357 * 10^18 = 2 weeks e-folding (time-constant) @ 361
- // blocks per tempo
- // After 2 weeks -> EMA reaches 63.2% of a step change
- // After ~4 weeks -> 86.5%
- // After ~6 weeks -> 95%
+ // blocks per tempo
+ // After 2 weeks -> EMA reaches 63.2% of a step change
+ // After ~4 weeks -> 86.5%
+ // After ~6 weeks -> 95%
}
#[pallet::storage]
@@ -2704,7 +2729,7 @@ pub enum RateLimitKey {
pub trait ProxyInterface {
fn add_lease_beneficiary_proxy(beneficiary: &AccountId, lease: &AccountId) -> DispatchResult;
fn remove_lease_beneficiary_proxy(beneficiary: &AccountId, lease: &AccountId)
- -> DispatchResult;
+ -> DispatchResult;
}
impl ProxyInterface for () {
diff --git a/pallets/subtensor/src/tests/consensus_mode.rs b/pallets/subtensor/src/tests/consensus_mode.rs
new file mode 100644
index 0000000000..bdb6442516
--- /dev/null
+++ b/pallets/subtensor/src/tests/consensus_mode.rs
@@ -0,0 +1,421 @@
+#![allow(
+ clippy::arithmetic_side_effects,
+ clippy::expect_used,
+ clippy::indexing_slicing,
+ clippy::unwrap_used
+)]
+
+use frame_support::{assert_err, assert_ok};
+use sp_core::U256;
+use substrate_fixed::types::I32F32;
+use subtensor_runtime_common::NetUid;
+
+use super::mock::*;
+use crate::*;
+
+/// Test setting consensus mode when liquid alpha is disabled
+#[test]
+fn test_set_consensus_mode_liquid_alpha_disabled() {
+ new_test_ext(1).execute_with(|| {
+ let hotkey = U256::from(1);
+ let coldkey = U256::from(1 + 456);
+ let netuid = add_dynamic_network(&hotkey, &coldkey);
+ let signer = RuntimeOrigin::signed(coldkey);
+
+ // Setup network
+ migrations::migrate_create_root_network::migrate_create_root_network::();
+ SubtensorModule::add_balance_to_coldkey_account(&coldkey, 1_000_000_000_000_000);
+ assert_ok!(SubtensorModule::root_register(signer.clone(), hotkey));
+ assert_ok!(SubtensorModule::register_network(signer.clone(), hotkey));
+
+ // Liquid Alpha is disabled by default
+ assert!(!SubtensorModule::get_liquid_alpha_enabled(netuid));
+
+ // Should fail to set consensus mode when liquid alpha is disabled
+ assert_err!(
+ SubtensorModule::do_set_liquid_alpha_consensus_mode(
+ signer.clone(),
+ netuid,
+ ConsensusMode::Previous
+ ),
+ Error::::LiquidAlphaDisabled
+ );
+
+ // Enable Liquid Alpha
+ SubtensorModule::set_liquid_alpha_enabled(netuid, true);
+
+ // Should now succeed
+ assert_ok!(SubtensorModule::do_set_liquid_alpha_consensus_mode(
+ signer.clone(),
+ netuid,
+ ConsensusMode::Previous
+ ));
+ });
+}
+
+/// Test that only subnet owner or root can set consensus mode
+#[test]
+fn test_set_consensus_mode_permissions() {
+ new_test_ext(1).execute_with(|| {
+ let hotkey = U256::from(1);
+ let coldkey = U256::from(1 + 456);
+ let non_owner = U256::from(999);
+ let netuid = add_dynamic_network(&hotkey, &coldkey);
+ let owner_signer = RuntimeOrigin::signed(coldkey);
+ let non_owner_signer = RuntimeOrigin::signed(non_owner);
+
+ // Setup network
+ SubtensorModule::set_liquid_alpha_enabled(netuid, true);
+ migrations::migrate_create_root_network::migrate_create_root_network::();
+ SubtensorModule::add_balance_to_coldkey_account(&coldkey, 1_000_000_000_000_000);
+ assert_ok!(SubtensorModule::root_register(owner_signer.clone(), hotkey));
+ assert_ok!(SubtensorModule::register_network(
+ owner_signer.clone(),
+ hotkey
+ ));
+
+ // Non-owner should fail
+ assert_err!(
+ SubtensorModule::do_set_liquid_alpha_consensus_mode(
+ non_owner_signer,
+ netuid,
+ ConsensusMode::Previous
+ ),
+ DispatchError::BadOrigin
+ );
+
+ // Owner should succeed
+ assert_ok!(SubtensorModule::do_set_liquid_alpha_consensus_mode(
+ owner_signer,
+ netuid,
+ ConsensusMode::Previous
+ ));
+ });
+}
+
+/// Test default consensus mode is Auto
+#[test]
+fn test_default_consensus_mode() {
+ new_test_ext(1).execute_with(|| {
+ let hotkey = U256::from(1);
+ let coldkey = U256::from(1 + 456);
+ let netuid = add_dynamic_network(&hotkey, &coldkey);
+
+ // Default should be Auto
+ let mode = SubtensorModule::get_liquid_alpha_consensus_mode(netuid);
+ assert_eq!(mode, ConsensusMode::Auto);
+ });
+}
+
+/// Test setting and getting all consensus modes
+#[test]
+fn test_set_and_get_consensus_modes() {
+ new_test_ext(1).execute_with(|| {
+ let hotkey = U256::from(1);
+ let coldkey = U256::from(1 + 456);
+ let netuid = add_dynamic_network(&hotkey, &coldkey);
+ let signer = RuntimeOrigin::signed(coldkey);
+
+ // Setup network
+ SubtensorModule::set_liquid_alpha_enabled(netuid, true);
+ migrations::migrate_create_root_network::migrate_create_root_network::();
+ SubtensorModule::add_balance_to_coldkey_account(&coldkey, 1_000_000_000_000_000);
+ assert_ok!(SubtensorModule::root_register(signer.clone(), hotkey));
+ assert_ok!(SubtensorModule::register_network(signer.clone(), hotkey));
+
+ // Test Current mode
+ assert_ok!(SubtensorModule::do_set_liquid_alpha_consensus_mode(
+ signer.clone(),
+ netuid,
+ ConsensusMode::Current
+ ));
+ assert_eq!(
+ SubtensorModule::get_liquid_alpha_consensus_mode(netuid),
+ ConsensusMode::Current
+ );
+
+ // Test Previous mode
+ assert_ok!(SubtensorModule::do_set_liquid_alpha_consensus_mode(
+ signer.clone(),
+ netuid,
+ ConsensusMode::Previous
+ ));
+ assert_eq!(
+ SubtensorModule::get_liquid_alpha_consensus_mode(netuid),
+ ConsensusMode::Previous
+ );
+
+ // Test Max mode
+ assert_ok!(SubtensorModule::do_set_liquid_alpha_consensus_mode(
+ signer.clone(),
+ netuid,
+ ConsensusMode::Max
+ ));
+ assert_eq!(
+ SubtensorModule::get_liquid_alpha_consensus_mode(netuid),
+ ConsensusMode::Max
+ );
+
+ // Test Auto mode
+ assert_ok!(SubtensorModule::do_set_liquid_alpha_consensus_mode(
+ signer.clone(),
+ netuid,
+ ConsensusMode::Auto
+ ));
+ assert_eq!(
+ SubtensorModule::get_liquid_alpha_consensus_mode(netuid),
+ ConsensusMode::Auto
+ );
+ });
+}
+
+/// Test compute_consensus_for_liquid_alpha with Current mode
+#[test]
+fn test_compute_consensus_current_mode() {
+ new_test_ext(1).execute_with(|| {
+ let netuid: NetUid = 1.into();
+ let n: usize = 4;
+
+ // Create network
+ add_network(netuid, 0, 0);
+ SubtensorModule::set_liquid_alpha_enabled(netuid, true);
+ SubtensorModule::set_liquid_alpha_consensus_mode(netuid, ConsensusMode::Current);
+
+ // Setup some current consensus values
+ let current_consensus: Vec = vec![
+ I32F32::from_num(0.2),
+ I32F32::from_num(0.3),
+ I32F32::from_num(0.4),
+ I32F32::from_num(0.1),
+ ];
+
+ // Store some different previous consensus values in storage
+ let previous_consensus_u16: Vec = vec![
+ (0.1 * u16::MAX as f32) as u16,
+ (0.2 * u16::MAX as f32) as u16,
+ (0.3 * u16::MAX as f32) as u16,
+ (0.4 * u16::MAX as f32) as u16,
+ ];
+ Consensus::::insert(netuid, previous_consensus_u16);
+
+ // Compute consensus for liquid alpha
+ let result = SubtensorModule::compute_consensus_for_liquid_alpha(netuid, ¤t_consensus);
+
+ // Should return current consensus (not previous)
+ assert_eq!(result.len(), n);
+ for i in 0..n {
+ assert_eq!(result[i], current_consensus[i]);
+ }
+ });
+}
+
+/// Test compute_consensus_for_liquid_alpha with Previous mode
+#[test]
+fn test_compute_consensus_previous_mode() {
+ new_test_ext(1).execute_with(|| {
+ let netuid: NetUid = 1.into();
+ let n: usize = 4;
+
+ // Create network
+ add_network(netuid, 0, 0);
+ SubtensorModule::set_liquid_alpha_enabled(netuid, true);
+ SubtensorModule::set_liquid_alpha_consensus_mode(netuid, ConsensusMode::Previous);
+
+ // Setup some current consensus values
+ let current_consensus: Vec = vec![
+ I32F32::from_num(0.2),
+ I32F32::from_num(0.3),
+ I32F32::from_num(0.4),
+ I32F32::from_num(0.1),
+ ];
+
+ // Store some different previous consensus values in storage
+ let previous_values: Vec = vec![0.1, 0.2, 0.3, 0.4];
+ let previous_consensus_u16: Vec = previous_values
+ .iter()
+ .map(|&v| (v * u16::MAX as f32) as u16)
+ .collect();
+ Consensus::::insert(netuid, previous_consensus_u16.clone());
+
+ // Compute consensus for liquid alpha
+ let result = SubtensorModule::compute_consensus_for_liquid_alpha(netuid, ¤t_consensus);
+
+ // Should return previous consensus from storage (not current)
+ assert_eq!(result.len(), n);
+ for i in 0..n {
+ let expected = I32F32::from_num(previous_values[i]);
+ // Allow small floating point difference
+ let diff = if result[i] > expected {
+ result[i] - expected
+ } else {
+ expected - result[i]
+ };
+ assert!(diff < I32F32::from_num(0.001), "Values should be approximately equal");
+ }
+ });
+}
+
+/// Test compute_consensus_for_liquid_alpha with Max mode
+#[test]
+fn test_compute_consensus_max_mode() {
+ new_test_ext(1).execute_with(|| {
+ let netuid: NetUid = 1.into();
+ let n: usize = 4;
+
+ // Create network
+ add_network(netuid, 0, 0);
+ SubtensorModule::set_liquid_alpha_enabled(netuid, true);
+ SubtensorModule::set_liquid_alpha_consensus_mode(netuid, ConsensusMode::Max);
+
+ // Setup current consensus values (some larger, some smaller than previous)
+ let current_consensus: Vec = vec![
+ I32F32::from_num(0.2), // larger than 0.1
+ I32F32::from_num(0.2), // same as 0.2
+ I32F32::from_num(0.25), // smaller than 0.3
+ I32F32::from_num(0.5), // larger than 0.4
+ ];
+
+ // Store previous consensus values in storage
+ let previous_values: Vec = vec![0.1, 0.2, 0.3, 0.4];
+ let previous_consensus_u16: Vec = previous_values
+ .iter()
+ .map(|&v| (v * u16::MAX as f32) as u16)
+ .collect();
+ Consensus::::insert(netuid, previous_consensus_u16);
+
+ // Compute consensus for liquid alpha
+ let result = SubtensorModule::compute_consensus_for_liquid_alpha(netuid, ¤t_consensus);
+
+ // Should return element-wise max of current and previous
+ assert_eq!(result.len(), n);
+
+ // Expected max values
+ let expected: Vec = vec![0.2, 0.2, 0.3, 0.5];
+ for i in 0..n {
+ let expected_val = I32F32::from_num(expected[i]);
+ let diff = if result[i] > expected_val {
+ result[i] - expected_val
+ } else {
+ expected_val - result[i]
+ };
+ assert!(diff < I32F32::from_num(0.001), "Values should be approximately equal at index {}", i);
+ }
+ });
+}
+
+/// Test compute_consensus_for_liquid_alpha with Max mode when network grows
+/// (current consensus is longer than previous consensus)
+#[test]
+fn test_compute_consensus_max_mode_network_growth() {
+ new_test_ext(1).execute_with(|| {
+ let netuid: NetUid = 1.into();
+
+ // Create network
+ add_network(netuid, 0, 0);
+ SubtensorModule::set_liquid_alpha_enabled(netuid, true);
+ SubtensorModule::set_liquid_alpha_consensus_mode(netuid, ConsensusMode::Max);
+
+ // Setup current consensus with 6 values (network has grown)
+ let current_consensus: Vec = vec![
+ I32F32::from_num(0.2), // larger than 0.1
+ I32F32::from_num(0.15), // smaller than 0.2
+ I32F32::from_num(0.25), // smaller than 0.3
+ I32F32::from_num(0.5), // larger than 0.4
+ I32F32::from_num(0.1), // new position (no previous) - should use current
+ I32F32::from_num(0.3), // new position (no previous) - should use current
+ ];
+
+ // Store previous consensus values in storage (only 4 values - network was smaller)
+ let previous_values: Vec = vec![0.1, 0.2, 0.3, 0.4];
+ let previous_consensus_u16: Vec = previous_values
+ .iter()
+ .map(|&v| (v * u16::MAX as f32) as u16)
+ .collect();
+ Consensus::::insert(netuid, previous_consensus_u16);
+
+ // Compute consensus for liquid alpha
+ let result = SubtensorModule::compute_consensus_for_liquid_alpha(netuid, ¤t_consensus);
+
+ // Should return element-wise max of current and previous
+ // For positions where previous doesn't exist, treat previous as 0 (so use current)
+ assert_eq!(result.len(), 6);
+
+ // Expected max values:
+ // [0] max(0.2, 0.1) = 0.2
+ // [1] max(0.15, 0.2) = 0.2
+ // [2] max(0.25, 0.3) = 0.3
+ // [3] max(0.5, 0.4) = 0.5
+ // [4] max(0.1, 0) = 0.1 (no previous, treat as 0)
+ // [5] max(0.3, 0) = 0.3 (no previous, treat as 0)
+ let expected: Vec = vec![0.2, 0.2, 0.3, 0.5, 0.1, 0.3];
+ for i in 0..6 {
+ let expected_val = I32F32::from_num(expected[i]);
+ let diff = if result[i] > expected_val {
+ result[i] - expected_val
+ } else {
+ expected_val - result[i]
+ };
+ assert!(
+ diff < I32F32::from_num(0.001),
+ "Values should be approximately equal at index {}. Expected: {}, Got: {}",
+ i,
+ expected[i],
+ result[i]
+ );
+ }
+ });
+}
+
+/// Test compute_consensus_for_liquid_alpha with Auto mode
+#[test]
+fn test_compute_consensus_auto_mode() {
+ new_test_ext(1).execute_with(|| {
+ let netuid: NetUid = 1.into();
+ let n: usize = 4;
+
+ // Create network
+ add_network(netuid, 0, 0);
+ SubtensorModule::set_liquid_alpha_enabled(netuid, true);
+ SubtensorModule::set_liquid_alpha_consensus_mode(netuid, ConsensusMode::Auto);
+
+ // Setup current consensus values
+ let current_consensus: Vec = vec![
+ I32F32::from_num(0.2),
+ I32F32::from_num(0.3),
+ I32F32::from_num(0.4),
+ I32F32::from_num(0.1),
+ ];
+
+ // Store previous consensus values in storage
+ let previous_values: Vec = vec![0.1, 0.2, 0.3, 0.4];
+ let previous_consensus_u16: Vec = previous_values
+ .iter()
+ .map(|&v| (v * u16::MAX as f32) as u16)
+ .collect();
+ Consensus::::insert(netuid, previous_consensus_u16);
+
+ // Test 1: bond_penalty != 1, should use Current
+ SubtensorModule::set_bonds_penalty(netuid, u16::MAX / 2); // 0.5
+ let result = SubtensorModule::compute_consensus_for_liquid_alpha(netuid, ¤t_consensus);
+
+ assert_eq!(result.len(), n);
+ for i in 0..n {
+ assert_eq!(result[i], current_consensus[i], "Should use current consensus when bond_penalty != 1");
+ }
+
+ // Test 2: bond_penalty == 1, should use Previous
+ SubtensorModule::set_bonds_penalty(netuid, u16::MAX); // 1.0
+ let result = SubtensorModule::compute_consensus_for_liquid_alpha(netuid, ¤t_consensus);
+
+ assert_eq!(result.len(), n);
+ for i in 0..n {
+ let expected = I32F32::from_num(previous_values[i]);
+ let diff = if result[i] > expected {
+ result[i] - expected
+ } else {
+ expected - result[i]
+ };
+ assert!(diff < I32F32::from_num(0.001), "Should use previous consensus when bond_penalty == 1");
+ }
+ });
+}
\ No newline at end of file
diff --git a/pallets/subtensor/src/tests/epoch.rs b/pallets/subtensor/src/tests/epoch.rs
index 3775efbea0..db32f0c7b0 100644
--- a/pallets/subtensor/src/tests/epoch.rs
+++ b/pallets/subtensor/src/tests/epoch.rs
@@ -3555,6 +3555,8 @@ fn test_liquid_alpha_equal_values_against_itself() {
// compute bonds with liquid alpha enabled
SubtensorModule::set_liquid_alpha_enabled(netuid.into(), true);
+ // Set consensus mode to Current to match the original behavior (using in-memory consensus)
+ SubtensorModule::set_liquid_alpha_consensus_mode(netuid.into(), ConsensusMode::Current);
let new_bonds_liquid_alpha_on =
SubtensorModule::compute_bonds(netuid.into(), &weights, &bonds, &consensus);
diff --git a/pallets/subtensor/src/tests/mod.rs b/pallets/subtensor/src/tests/mod.rs
index 8f07572e25..c9bee21fbe 100644
--- a/pallets/subtensor/src/tests/mod.rs
+++ b/pallets/subtensor/src/tests/mod.rs
@@ -4,6 +4,7 @@ mod children;
mod claim_root;
mod coinbase;
mod consensus;
+mod consensus_mode;
mod delegate_info;
mod difficulty;
mod emission;
diff --git a/pallets/subtensor/src/utils/misc.rs b/pallets/subtensor/src/utils/misc.rs
index 45462ec600..68bce62b9a 100644
--- a/pallets/subtensor/src/utils/misc.rs
+++ b/pallets/subtensor/src/utils/misc.rs
@@ -776,6 +776,14 @@ impl Pallet {
(converted_low, converted_high)
}
+ pub fn get_liquid_alpha_consensus_mode(netuid: NetUid) -> ConsensusMode {
+ LiquidAlphaConsensusMode::::get(netuid)
+ }
+
+ pub fn set_liquid_alpha_consensus_mode(netuid: NetUid, mode: ConsensusMode) {
+ LiquidAlphaConsensusMode::::insert(netuid, mode);
+ }
+
pub fn set_alpha_sigmoid_steepness(netuid: NetUid, steepness: i16) {
AlphaSigmoidSteepness::::insert(netuid, steepness);
}
From 789097464fb202947aecf9844bc5c0609d552428 Mon Sep 17 00:00:00 2001
From: konrad0960
Date: Tue, 11 Nov 2025 21:30:56 +0100
Subject: [PATCH 09/16] commit Cargo.lock
---
pallets/subtensor/src/epoch/run_epoch.rs | 19 --
pallets/subtensor/src/lib.rs | 2 -
pallets/subtensor/src/tests/consensus_mode.rs | 314 +++++-------------
3 files changed, 81 insertions(+), 254 deletions(-)
diff --git a/pallets/subtensor/src/epoch/run_epoch.rs b/pallets/subtensor/src/epoch/run_epoch.rs
index f8414864a7..c9ee974cee 100644
--- a/pallets/subtensor/src/epoch/run_epoch.rs
+++ b/pallets/subtensor/src/epoch/run_epoch.rs
@@ -1368,25 +1368,6 @@ impl Pallet {
.map(|&c| I32F32::saturating_from_num(c).safe_div(I32F32::saturating_from_num(u16::MAX)))
.collect()
}
- ConsensusMode::Max => {
- // Use element-wise max of current and previous
- let previous_consensus_u16 = Consensus::::get(netuid);
- let previous_consensus: Vec = previous_consensus_u16
- .iter()
- .map(|&c| I32F32::saturating_from_num(c).safe_div(I32F32::saturating_from_num(u16::MAX)))
- .collect();
-
- // For each position: max(current, previous)
- // If previous doesn't exist at that index (network grew), treat previous as 0
- current_consensus
- .iter()
- .enumerate()
- .map(|(i, &curr)| {
- let prev = previous_consensus.get(i).copied().unwrap_or(I32F32::from_num(0));
- if curr > prev { curr } else { prev }
- })
- .collect()
- }
ConsensusMode::Auto => {
// Auto mode: Previous if bond_penalty == 1, otherwise Current
if bonds_penalty == I32F32::from_num(1) {
diff --git a/pallets/subtensor/src/lib.rs b/pallets/subtensor/src/lib.rs
index 459403da32..6d6205a64b 100644
--- a/pallets/subtensor/src/lib.rs
+++ b/pallets/subtensor/src/lib.rs
@@ -355,8 +355,6 @@ pub mod pallet {
Current,
/// Use previous consensus from storage
Previous,
- /// Use max of current and previous consensus
- Max,
/// Auto mode: Previous if bond_penalty == 1, otherwise Current
#[default]
Auto,
diff --git a/pallets/subtensor/src/tests/consensus_mode.rs b/pallets/subtensor/src/tests/consensus_mode.rs
index bdb6442516..8ca6a8ec33 100644
--- a/pallets/subtensor/src/tests/consensus_mode.rs
+++ b/pallets/subtensor/src/tests/consensus_mode.rs
@@ -1,32 +1,68 @@
-#![allow(
- clippy::arithmetic_side_effects,
- clippy::expect_used,
- clippy::indexing_slicing,
- clippy::unwrap_used
-)]
-
use frame_support::{assert_err, assert_ok};
use sp_core::U256;
use substrate_fixed::types::I32F32;
use subtensor_runtime_common::NetUid;
+use super::consensus::{fixed, fixed_proportion_to_u16};
use super::mock::*;
use crate::*;
+// ============================================================================
+// Test Helper Functions
+// ============================================================================
+
+/// Sets up a network with full owner permissions (root registration + network creation)
+/// Returns (netuid, hotkey, coldkey, signer)
+fn setup_network_with_owner() -> (NetUid, U256, U256, RuntimeOrigin) {
+ let hotkey = U256::from(1);
+ let coldkey = U256::from(457);
+ let netuid = add_dynamic_network(&hotkey, &coldkey);
+ let signer = RuntimeOrigin::signed(coldkey);
+
+ migrations::migrate_create_root_network::migrate_create_root_network::();
+ SubtensorModule::add_balance_to_coldkey_account(&coldkey, 1_000_000_000_000_000);
+ assert_ok!(SubtensorModule::root_register(signer.clone(), hotkey));
+ assert_ok!(SubtensorModule::register_network(signer.clone(), hotkey));
+
+ (netuid, hotkey, coldkey, signer)
+}
+
+/// Sets up a basic network environment for consensus testing
+/// Creates network and enables liquid alpha
+fn setup_consensus_test_environment(netuid: NetUid) {
+ add_network(netuid, 0, 0);
+ SubtensorModule::set_liquid_alpha_enabled(netuid, true);
+}
+
+/// Creates test consensus data and stores it in the system
+/// Returns (current_consensus, previous_values)
+fn create_test_consensus_data(netuid: NetUid) -> (Vec, Vec) {
+ let current_consensus: Vec = vec![
+ I32F32::from_num(0.2),
+ I32F32::from_num(0.3),
+ I32F32::from_num(0.4),
+ I32F32::from_num(0.1),
+ ];
+
+ let previous_values: Vec = vec![0.1, 0.2, 0.3, 0.4];
+ let previous_consensus_u16: Vec = previous_values
+ .iter()
+ .map(|&v| fixed_proportion_to_u16(fixed(v)))
+ .collect();
+ Consensus::::insert(netuid, previous_consensus_u16);
+
+ (current_consensus, previous_values)
+}
+
+// ============================================================================
+// Tests
+// ============================================================================
+
/// Test setting consensus mode when liquid alpha is disabled
#[test]
fn test_set_consensus_mode_liquid_alpha_disabled() {
new_test_ext(1).execute_with(|| {
- let hotkey = U256::from(1);
- let coldkey = U256::from(1 + 456);
- let netuid = add_dynamic_network(&hotkey, &coldkey);
- let signer = RuntimeOrigin::signed(coldkey);
-
- // Setup network
- migrations::migrate_create_root_network::migrate_create_root_network::();
- SubtensorModule::add_balance_to_coldkey_account(&coldkey, 1_000_000_000_000_000);
- assert_ok!(SubtensorModule::root_register(signer.clone(), hotkey));
- assert_ok!(SubtensorModule::register_network(signer.clone(), hotkey));
+ let (netuid, _hotkey, _coldkey, signer) = setup_network_with_owner();
// Liquid Alpha is disabled by default
assert!(!SubtensorModule::get_liquid_alpha_enabled(netuid));
@@ -57,22 +93,12 @@ fn test_set_consensus_mode_liquid_alpha_disabled() {
#[test]
fn test_set_consensus_mode_permissions() {
new_test_ext(1).execute_with(|| {
- let hotkey = U256::from(1);
- let coldkey = U256::from(1 + 456);
+ let (netuid, _hotkey, _coldkey, owner_signer) = setup_network_with_owner();
let non_owner = U256::from(999);
- let netuid = add_dynamic_network(&hotkey, &coldkey);
- let owner_signer = RuntimeOrigin::signed(coldkey);
let non_owner_signer = RuntimeOrigin::signed(non_owner);
- // Setup network
+ // Enable liquid alpha for this test
SubtensorModule::set_liquid_alpha_enabled(netuid, true);
- migrations::migrate_create_root_network::migrate_create_root_network::();
- SubtensorModule::add_balance_to_coldkey_account(&coldkey, 1_000_000_000_000_000);
- assert_ok!(SubtensorModule::root_register(owner_signer.clone(), hotkey));
- assert_ok!(SubtensorModule::register_network(
- owner_signer.clone(),
- hotkey
- ));
// Non-owner should fail
assert_err!(
@@ -98,7 +124,7 @@ fn test_set_consensus_mode_permissions() {
fn test_default_consensus_mode() {
new_test_ext(1).execute_with(|| {
let hotkey = U256::from(1);
- let coldkey = U256::from(1 + 456);
+ let coldkey = U256::from(457);
let netuid = add_dynamic_network(&hotkey, &coldkey);
// Default should be Auto
@@ -111,17 +137,10 @@ fn test_default_consensus_mode() {
#[test]
fn test_set_and_get_consensus_modes() {
new_test_ext(1).execute_with(|| {
- let hotkey = U256::from(1);
- let coldkey = U256::from(1 + 456);
- let netuid = add_dynamic_network(&hotkey, &coldkey);
- let signer = RuntimeOrigin::signed(coldkey);
+ let (netuid, _hotkey, _coldkey, signer) = setup_network_with_owner();
- // Setup network
+ // Enable liquid alpha for this test
SubtensorModule::set_liquid_alpha_enabled(netuid, true);
- migrations::migrate_create_root_network::migrate_create_root_network::();
- SubtensorModule::add_balance_to_coldkey_account(&coldkey, 1_000_000_000_000_000);
- assert_ok!(SubtensorModule::root_register(signer.clone(), hotkey));
- assert_ok!(SubtensorModule::register_network(signer.clone(), hotkey));
// Test Current mode
assert_ok!(SubtensorModule::do_set_liquid_alpha_consensus_mode(
@@ -145,17 +164,6 @@ fn test_set_and_get_consensus_modes() {
ConsensusMode::Previous
);
- // Test Max mode
- assert_ok!(SubtensorModule::do_set_liquid_alpha_consensus_mode(
- signer.clone(),
- netuid,
- ConsensusMode::Max
- ));
- assert_eq!(
- SubtensorModule::get_liquid_alpha_consensus_mode(netuid),
- ConsensusMode::Max
- );
-
// Test Auto mode
assert_ok!(SubtensorModule::do_set_liquid_alpha_consensus_mode(
signer.clone(),
@@ -176,35 +184,18 @@ fn test_compute_consensus_current_mode() {
let netuid: NetUid = 1.into();
let n: usize = 4;
- // Create network
- add_network(netuid, 0, 0);
- SubtensorModule::set_liquid_alpha_enabled(netuid, true);
+ // Setup network and test data
+ setup_consensus_test_environment(netuid);
SubtensorModule::set_liquid_alpha_consensus_mode(netuid, ConsensusMode::Current);
-
- // Setup some current consensus values
- let current_consensus: Vec = vec![
- I32F32::from_num(0.2),
- I32F32::from_num(0.3),
- I32F32::from_num(0.4),
- I32F32::from_num(0.1),
- ];
-
- // Store some different previous consensus values in storage
- let previous_consensus_u16: Vec = vec![
- (0.1 * u16::MAX as f32) as u16,
- (0.2 * u16::MAX as f32) as u16,
- (0.3 * u16::MAX as f32) as u16,
- (0.4 * u16::MAX as f32) as u16,
- ];
- Consensus::::insert(netuid, previous_consensus_u16);
+ let (current_consensus, _previous_values) = create_test_consensus_data(netuid);
// Compute consensus for liquid alpha
let result = SubtensorModule::compute_consensus_for_liquid_alpha(netuid, ¤t_consensus);
// Should return current consensus (not previous)
assert_eq!(result.len(), n);
- for i in 0..n {
- assert_eq!(result[i], current_consensus[i]);
+ for (res, curr) in result.iter().zip(current_consensus.iter()) {
+ assert_eq!(res, curr);
}
});
}
@@ -216,156 +207,29 @@ fn test_compute_consensus_previous_mode() {
let netuid: NetUid = 1.into();
let n: usize = 4;
- // Create network
- add_network(netuid, 0, 0);
- SubtensorModule::set_liquid_alpha_enabled(netuid, true);
+ // Setup network and test data
+ setup_consensus_test_environment(netuid);
SubtensorModule::set_liquid_alpha_consensus_mode(netuid, ConsensusMode::Previous);
-
- // Setup some current consensus values
- let current_consensus: Vec = vec![
- I32F32::from_num(0.2),
- I32F32::from_num(0.3),
- I32F32::from_num(0.4),
- I32F32::from_num(0.1),
- ];
-
- // Store some different previous consensus values in storage
- let previous_values: Vec = vec![0.1, 0.2, 0.3, 0.4];
- let previous_consensus_u16: Vec = previous_values
- .iter()
- .map(|&v| (v * u16::MAX as f32) as u16)
- .collect();
- Consensus::::insert(netuid, previous_consensus_u16.clone());
+ let (current_consensus, previous_values) = create_test_consensus_data(netuid);
// Compute consensus for liquid alpha
let result = SubtensorModule::compute_consensus_for_liquid_alpha(netuid, ¤t_consensus);
// Should return previous consensus from storage (not current)
assert_eq!(result.len(), n);
- for i in 0..n {
- let expected = I32F32::from_num(previous_values[i]);
+ for (res, &prev) in result.iter().zip(previous_values.iter()) {
+ let expected = I32F32::from_num(prev);
// Allow small floating point difference
- let diff = if result[i] > expected {
- result[i] - expected
+ let diff = if *res > expected {
+ *res - expected
} else {
- expected - result[i]
+ expected - *res
};
assert!(diff < I32F32::from_num(0.001), "Values should be approximately equal");
}
});
}
-/// Test compute_consensus_for_liquid_alpha with Max mode
-#[test]
-fn test_compute_consensus_max_mode() {
- new_test_ext(1).execute_with(|| {
- let netuid: NetUid = 1.into();
- let n: usize = 4;
-
- // Create network
- add_network(netuid, 0, 0);
- SubtensorModule::set_liquid_alpha_enabled(netuid, true);
- SubtensorModule::set_liquid_alpha_consensus_mode(netuid, ConsensusMode::Max);
-
- // Setup current consensus values (some larger, some smaller than previous)
- let current_consensus: Vec = vec![
- I32F32::from_num(0.2), // larger than 0.1
- I32F32::from_num(0.2), // same as 0.2
- I32F32::from_num(0.25), // smaller than 0.3
- I32F32::from_num(0.5), // larger than 0.4
- ];
-
- // Store previous consensus values in storage
- let previous_values: Vec = vec![0.1, 0.2, 0.3, 0.4];
- let previous_consensus_u16: Vec = previous_values
- .iter()
- .map(|&v| (v * u16::MAX as f32) as u16)
- .collect();
- Consensus::::insert(netuid, previous_consensus_u16);
-
- // Compute consensus for liquid alpha
- let result = SubtensorModule::compute_consensus_for_liquid_alpha(netuid, ¤t_consensus);
-
- // Should return element-wise max of current and previous
- assert_eq!(result.len(), n);
-
- // Expected max values
- let expected: Vec = vec![0.2, 0.2, 0.3, 0.5];
- for i in 0..n {
- let expected_val = I32F32::from_num(expected[i]);
- let diff = if result[i] > expected_val {
- result[i] - expected_val
- } else {
- expected_val - result[i]
- };
- assert!(diff < I32F32::from_num(0.001), "Values should be approximately equal at index {}", i);
- }
- });
-}
-
-/// Test compute_consensus_for_liquid_alpha with Max mode when network grows
-/// (current consensus is longer than previous consensus)
-#[test]
-fn test_compute_consensus_max_mode_network_growth() {
- new_test_ext(1).execute_with(|| {
- let netuid: NetUid = 1.into();
-
- // Create network
- add_network(netuid, 0, 0);
- SubtensorModule::set_liquid_alpha_enabled(netuid, true);
- SubtensorModule::set_liquid_alpha_consensus_mode(netuid, ConsensusMode::Max);
-
- // Setup current consensus with 6 values (network has grown)
- let current_consensus: Vec = vec![
- I32F32::from_num(0.2), // larger than 0.1
- I32F32::from_num(0.15), // smaller than 0.2
- I32F32::from_num(0.25), // smaller than 0.3
- I32F32::from_num(0.5), // larger than 0.4
- I32F32::from_num(0.1), // new position (no previous) - should use current
- I32F32::from_num(0.3), // new position (no previous) - should use current
- ];
-
- // Store previous consensus values in storage (only 4 values - network was smaller)
- let previous_values: Vec = vec![0.1, 0.2, 0.3, 0.4];
- let previous_consensus_u16: Vec = previous_values
- .iter()
- .map(|&v| (v * u16::MAX as f32) as u16)
- .collect();
- Consensus::::insert(netuid, previous_consensus_u16);
-
- // Compute consensus for liquid alpha
- let result = SubtensorModule::compute_consensus_for_liquid_alpha(netuid, ¤t_consensus);
-
- // Should return element-wise max of current and previous
- // For positions where previous doesn't exist, treat previous as 0 (so use current)
- assert_eq!(result.len(), 6);
-
- // Expected max values:
- // [0] max(0.2, 0.1) = 0.2
- // [1] max(0.15, 0.2) = 0.2
- // [2] max(0.25, 0.3) = 0.3
- // [3] max(0.5, 0.4) = 0.5
- // [4] max(0.1, 0) = 0.1 (no previous, treat as 0)
- // [5] max(0.3, 0) = 0.3 (no previous, treat as 0)
- let expected: Vec = vec![0.2, 0.2, 0.3, 0.5, 0.1, 0.3];
- for i in 0..6 {
- let expected_val = I32F32::from_num(expected[i]);
- let diff = if result[i] > expected_val {
- result[i] - expected_val
- } else {
- expected_val - result[i]
- };
- assert!(
- diff < I32F32::from_num(0.001),
- "Values should be approximately equal at index {}. Expected: {}, Got: {}",
- i,
- expected[i],
- result[i]
- );
- }
- });
-}
-
/// Test compute_consensus_for_liquid_alpha with Auto mode
#[test]
fn test_compute_consensus_auto_mode() {
@@ -373,34 +237,18 @@ fn test_compute_consensus_auto_mode() {
let netuid: NetUid = 1.into();
let n: usize = 4;
- // Create network
- add_network(netuid, 0, 0);
- SubtensorModule::set_liquid_alpha_enabled(netuid, true);
+ // Setup network and test data
+ setup_consensus_test_environment(netuid);
SubtensorModule::set_liquid_alpha_consensus_mode(netuid, ConsensusMode::Auto);
-
- // Setup current consensus values
- let current_consensus: Vec = vec![
- I32F32::from_num(0.2),
- I32F32::from_num(0.3),
- I32F32::from_num(0.4),
- I32F32::from_num(0.1),
- ];
-
- // Store previous consensus values in storage
- let previous_values: Vec = vec![0.1, 0.2, 0.3, 0.4];
- let previous_consensus_u16: Vec = previous_values
- .iter()
- .map(|&v| (v * u16::MAX as f32) as u16)
- .collect();
- Consensus::::insert(netuid, previous_consensus_u16);
+ let (current_consensus, previous_values) = create_test_consensus_data(netuid);
// Test 1: bond_penalty != 1, should use Current
SubtensorModule::set_bonds_penalty(netuid, u16::MAX / 2); // 0.5
let result = SubtensorModule::compute_consensus_for_liquid_alpha(netuid, ¤t_consensus);
assert_eq!(result.len(), n);
- for i in 0..n {
- assert_eq!(result[i], current_consensus[i], "Should use current consensus when bond_penalty != 1");
+ for (res, curr) in result.iter().zip(current_consensus.iter()) {
+ assert_eq!(res, curr, "Should use current consensus when bond_penalty != 1");
}
// Test 2: bond_penalty == 1, should use Previous
@@ -408,12 +256,12 @@ fn test_compute_consensus_auto_mode() {
let result = SubtensorModule::compute_consensus_for_liquid_alpha(netuid, ¤t_consensus);
assert_eq!(result.len(), n);
- for i in 0..n {
- let expected = I32F32::from_num(previous_values[i]);
- let diff = if result[i] > expected {
- result[i] - expected
+ for (res, &prev) in result.iter().zip(previous_values.iter()) {
+ let expected = I32F32::from_num(prev);
+ let diff = if *res > expected {
+ *res - expected
} else {
- expected - result[i]
+ expected - *res
};
assert!(diff < I32F32::from_num(0.001), "Should use previous consensus when bond_penalty == 1");
}
From 1d61d68fdc32e0dbe5c9f218a316e5f9a0fa8c2e Mon Sep 17 00:00:00 2001
From: konrad0960
Date: Tue, 11 Nov 2025 22:52:29 +0100
Subject: [PATCH 10/16] cargo fmt
---
pallets/subtensor/src/epoch/run_epoch.rs | 46 +++++++++++++------
pallets/subtensor/src/tests/consensus_mode.rs | 29 ++++++++----
2 files changed, 54 insertions(+), 21 deletions(-)
diff --git a/pallets/subtensor/src/epoch/run_epoch.rs b/pallets/subtensor/src/epoch/run_epoch.rs
index c9ee974cee..6620c203b7 100644
--- a/pallets/subtensor/src/epoch/run_epoch.rs
+++ b/pallets/subtensor/src/epoch/run_epoch.rs
@@ -1275,12 +1275,20 @@ impl Pallet {
.any(|&c| c != I32F32::saturating_from_num(0))
{
// Liquid Alpha is enabled, compute the appropriate consensus for liquid alpha based on mode
- let consensus_for_liquid_alpha = Self::compute_consensus_for_liquid_alpha(netuid, consensus);
- log::trace!("consensus_for_liquid_alpha: {:?}", &consensus_for_liquid_alpha);
+ let consensus_for_liquid_alpha =
+ Self::compute_consensus_for_liquid_alpha(netuid, consensus);
+ log::trace!(
+ "consensus_for_liquid_alpha: {:?}",
+ &consensus_for_liquid_alpha
+ );
// Compute the liquid alphas matrix.
- let alphas: Vec> =
- Self::compute_liquid_alpha_values(netuid, weights, bonds, &consensus_for_liquid_alpha);
+ let alphas: Vec> = Self::compute_liquid_alpha_values(
+ netuid,
+ weights,
+ bonds,
+ &consensus_for_liquid_alpha,
+ );
log::trace!("alphas: {:?}", &alphas);
// Compute the Exponential Moving Average (EMA) of bonds using the provided clamped alpha values.
@@ -1321,12 +1329,20 @@ impl Pallet {
.any(|&c| c != I32F32::saturating_from_num(0))
{
// Liquid Alpha is enabled, compute the appropriate consensus for liquid alpha based on mode
- let consensus_for_liquid_alpha = Self::compute_consensus_for_liquid_alpha(netuid, consensus);
- log::trace!("consensus_for_liquid_alpha: {:?}", &consensus_for_liquid_alpha);
+ let consensus_for_liquid_alpha =
+ Self::compute_consensus_for_liquid_alpha(netuid, consensus);
+ log::trace!(
+ "consensus_for_liquid_alpha: {:?}",
+ &consensus_for_liquid_alpha
+ );
// Compute the liquid alphas matrix.
- let alphas: Vec> =
- Self::compute_liquid_alpha_values_sparse(netuid, weights, bonds, &consensus_for_liquid_alpha);
+ let alphas: Vec> = Self::compute_liquid_alpha_values_sparse(
+ netuid,
+ weights,
+ bonds,
+ &consensus_for_liquid_alpha,
+ );
log::trace!("alphas: {:?}", &alphas);
// Compute the Exponential Moving Average (EMA) of bonds using the provided clamped alpha values.
@@ -1365,7 +1381,10 @@ impl Pallet {
let previous_consensus_u16 = Consensus::::get(netuid);
previous_consensus_u16
.iter()
- .map(|&c| I32F32::saturating_from_num(c).safe_div(I32F32::saturating_from_num(u16::MAX)))
+ .map(|&c| {
+ I32F32::saturating_from_num(c)
+ .safe_div(I32F32::saturating_from_num(u16::MAX))
+ })
.collect()
}
ConsensusMode::Auto => {
@@ -1374,7 +1393,10 @@ impl Pallet {
let previous_consensus_u16 = Consensus::::get(netuid);
previous_consensus_u16
.iter()
- .map(|&c| I32F32::saturating_from_num(c).safe_div(I32F32::saturating_from_num(u16::MAX)))
+ .map(|&c| {
+ I32F32::saturating_from_num(c)
+ .safe_div(I32F32::saturating_from_num(u16::MAX))
+ })
.collect()
} else {
current_consensus.to_vec()
@@ -1604,9 +1626,7 @@ impl Pallet {
Self::set_liquid_alpha_consensus_mode(netuid, mode.clone());
- log::debug!(
- "LiquidAlphaConsensusModeSet( netuid: {netuid:?}, mode: {mode:?} )",
- );
+ log::debug!("LiquidAlphaConsensusModeSet( netuid: {netuid:?}, mode: {mode:?} )",);
Ok(())
}
diff --git a/pallets/subtensor/src/tests/consensus_mode.rs b/pallets/subtensor/src/tests/consensus_mode.rs
index 8ca6a8ec33..500a76e808 100644
--- a/pallets/subtensor/src/tests/consensus_mode.rs
+++ b/pallets/subtensor/src/tests/consensus_mode.rs
@@ -190,7 +190,8 @@ fn test_compute_consensus_current_mode() {
let (current_consensus, _previous_values) = create_test_consensus_data(netuid);
// Compute consensus for liquid alpha
- let result = SubtensorModule::compute_consensus_for_liquid_alpha(netuid, ¤t_consensus);
+ let result =
+ SubtensorModule::compute_consensus_for_liquid_alpha(netuid, ¤t_consensus);
// Should return current consensus (not previous)
assert_eq!(result.len(), n);
@@ -213,7 +214,8 @@ fn test_compute_consensus_previous_mode() {
let (current_consensus, previous_values) = create_test_consensus_data(netuid);
// Compute consensus for liquid alpha
- let result = SubtensorModule::compute_consensus_for_liquid_alpha(netuid, ¤t_consensus);
+ let result =
+ SubtensorModule::compute_consensus_for_liquid_alpha(netuid, ¤t_consensus);
// Should return previous consensus from storage (not current)
assert_eq!(result.len(), n);
@@ -225,7 +227,10 @@ fn test_compute_consensus_previous_mode() {
} else {
expected - *res
};
- assert!(diff < I32F32::from_num(0.001), "Values should be approximately equal");
+ assert!(
+ diff < I32F32::from_num(0.001),
+ "Values should be approximately equal"
+ );
}
});
}
@@ -244,16 +249,21 @@ fn test_compute_consensus_auto_mode() {
// Test 1: bond_penalty != 1, should use Current
SubtensorModule::set_bonds_penalty(netuid, u16::MAX / 2); // 0.5
- let result = SubtensorModule::compute_consensus_for_liquid_alpha(netuid, ¤t_consensus);
+ let result =
+ SubtensorModule::compute_consensus_for_liquid_alpha(netuid, ¤t_consensus);
assert_eq!(result.len(), n);
for (res, curr) in result.iter().zip(current_consensus.iter()) {
- assert_eq!(res, curr, "Should use current consensus when bond_penalty != 1");
+ assert_eq!(
+ res, curr,
+ "Should use current consensus when bond_penalty != 1"
+ );
}
// Test 2: bond_penalty == 1, should use Previous
SubtensorModule::set_bonds_penalty(netuid, u16::MAX); // 1.0
- let result = SubtensorModule::compute_consensus_for_liquid_alpha(netuid, ¤t_consensus);
+ let result =
+ SubtensorModule::compute_consensus_for_liquid_alpha(netuid, ¤t_consensus);
assert_eq!(result.len(), n);
for (res, &prev) in result.iter().zip(previous_values.iter()) {
@@ -263,7 +273,10 @@ fn test_compute_consensus_auto_mode() {
} else {
expected - *res
};
- assert!(diff < I32F32::from_num(0.001), "Should use previous consensus when bond_penalty == 1");
+ assert!(
+ diff < I32F32::from_num(0.001),
+ "Should use previous consensus when bond_penalty == 1"
+ );
}
});
-}
\ No newline at end of file
+}
From be8ff6a4642d0f2c8f7fa6b8ee268ae0a9a5ef43 Mon Sep 17 00:00:00 2001
From: konrad0960
Date: Thu, 13 Nov 2025 21:59:52 +0100
Subject: [PATCH 11/16] commit Cargo.lock
---
Cargo.lock | 6 +--
pallets/subtensor/src/epoch/run_epoch.rs | 7 +--
pallets/subtensor/src/tests/consensus_mode.rs | 51 ++++++++-----------
3 files changed, 24 insertions(+), 40 deletions(-)
diff --git a/Cargo.lock b/Cargo.lock
index c582dd22cc..34b5a47400 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -3667,7 +3667,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8d162beedaa69905488a8da94f5ac3edb4dd4788b732fadb7bd120b2625c1976"
dependencies = [
"data-encoding",
- "syn 2.0.106",
+ "syn 1.0.109",
]
[[package]]
@@ -6451,7 +6451,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4b0f83760fb341a774ed326568e19f5a863af4a952def8c39f9ab92fd95b88e5"
dependencies = [
"equivalent",
- "hashbrown 0.16.0",
+ "hashbrown 0.15.5",
"serde",
"serde_core",
]
@@ -8663,7 +8663,7 @@ version = "0.7.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "77e878c846a8abae00dd069496dbe8751b16ac1c3d6bd2a7283a938e8228f90d"
dependencies = [
- "proc-macro-crate 3.4.0",
+ "proc-macro-crate 1.1.3",
"proc-macro2",
"quote",
"syn 2.0.106",
diff --git a/pallets/subtensor/src/epoch/run_epoch.rs b/pallets/subtensor/src/epoch/run_epoch.rs
index 6620c203b7..041dc77c4f 100644
--- a/pallets/subtensor/src/epoch/run_epoch.rs
+++ b/pallets/subtensor/src/epoch/run_epoch.rs
@@ -1369,7 +1369,6 @@ impl Pallet {
current_consensus: &[I32F32],
) -> Vec {
let mode = Self::get_liquid_alpha_consensus_mode(netuid);
- let bonds_penalty = Self::get_float_bonds_penalty(netuid);
match mode {
ConsensusMode::Current => {
@@ -1389,6 +1388,7 @@ impl Pallet {
}
ConsensusMode::Auto => {
// Auto mode: Previous if bond_penalty == 1, otherwise Current
+ let bonds_penalty = Self::get_float_bonds_penalty(netuid);
if bonds_penalty == I32F32::from_num(1) {
let previous_consensus_u16 = Consensus::::get(netuid);
previous_consensus_u16
@@ -1619,11 +1619,6 @@ impl Pallet {
) -> Result<(), DispatchError> {
Self::ensure_subnet_owner_or_root(origin, netuid)?;
- ensure!(
- Self::get_liquid_alpha_enabled(netuid),
- Error::::LiquidAlphaDisabled
- );
-
Self::set_liquid_alpha_consensus_mode(netuid, mode.clone());
log::debug!("LiquidAlphaConsensusModeSet( netuid: {netuid:?}, mode: {mode:?} )",);
diff --git a/pallets/subtensor/src/tests/consensus_mode.rs b/pallets/subtensor/src/tests/consensus_mode.rs
index 500a76e808..e9aeec6368 100644
--- a/pallets/subtensor/src/tests/consensus_mode.rs
+++ b/pallets/subtensor/src/tests/consensus_mode.rs
@@ -67,25 +67,34 @@ fn test_set_consensus_mode_liquid_alpha_disabled() {
// Liquid Alpha is disabled by default
assert!(!SubtensorModule::get_liquid_alpha_enabled(netuid));
- // Should fail to set consensus mode when liquid alpha is disabled
- assert_err!(
- SubtensorModule::do_set_liquid_alpha_consensus_mode(
- signer.clone(),
- netuid,
- ConsensusMode::Previous
- ),
- Error::::LiquidAlphaDisabled
+ // Should succeed to set consensus mode even when liquid alpha is disabled
+ assert_ok!(SubtensorModule::do_set_liquid_alpha_consensus_mode(
+ signer.clone(),
+ netuid,
+ ConsensusMode::Previous
+ ));
+
+ // Verify the mode was set
+ assert_eq!(
+ SubtensorModule::get_liquid_alpha_consensus_mode(netuid),
+ ConsensusMode::Previous
);
- // Enable Liquid Alpha
+ // Also verify with liquid alpha enabled
SubtensorModule::set_liquid_alpha_enabled(netuid, true);
- // Should now succeed
+ // Should still succeed
assert_ok!(SubtensorModule::do_set_liquid_alpha_consensus_mode(
signer.clone(),
netuid,
- ConsensusMode::Previous
+ ConsensusMode::Current
));
+
+ // Verify the mode was updated
+ assert_eq!(
+ SubtensorModule::get_liquid_alpha_consensus_mode(netuid),
+ ConsensusMode::Current
+ );
});
}
@@ -97,9 +106,6 @@ fn test_set_consensus_mode_permissions() {
let non_owner = U256::from(999);
let non_owner_signer = RuntimeOrigin::signed(non_owner);
- // Enable liquid alpha for this test
- SubtensorModule::set_liquid_alpha_enabled(netuid, true);
-
// Non-owner should fail
assert_err!(
SubtensorModule::do_set_liquid_alpha_consensus_mode(
@@ -119,29 +125,12 @@ fn test_set_consensus_mode_permissions() {
});
}
-/// Test default consensus mode is Auto
-#[test]
-fn test_default_consensus_mode() {
- new_test_ext(1).execute_with(|| {
- let hotkey = U256::from(1);
- let coldkey = U256::from(457);
- let netuid = add_dynamic_network(&hotkey, &coldkey);
-
- // Default should be Auto
- let mode = SubtensorModule::get_liquid_alpha_consensus_mode(netuid);
- assert_eq!(mode, ConsensusMode::Auto);
- });
-}
-
/// Test setting and getting all consensus modes
#[test]
fn test_set_and_get_consensus_modes() {
new_test_ext(1).execute_with(|| {
let (netuid, _hotkey, _coldkey, signer) = setup_network_with_owner();
- // Enable liquid alpha for this test
- SubtensorModule::set_liquid_alpha_enabled(netuid, true);
-
// Test Current mode
assert_ok!(SubtensorModule::do_set_liquid_alpha_consensus_mode(
signer.clone(),
From 95fb74ab294ddf507929ee6e25609eb578349db1 Mon Sep 17 00:00:00 2001
From: konrad0960
Date: Thu, 13 Nov 2025 22:17:07 +0100
Subject: [PATCH 12/16] Revert Cargo.lock to previous state
---
Cargo.lock | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/Cargo.lock b/Cargo.lock
index 34b5a47400..c582dd22cc 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -3667,7 +3667,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8d162beedaa69905488a8da94f5ac3edb4dd4788b732fadb7bd120b2625c1976"
dependencies = [
"data-encoding",
- "syn 1.0.109",
+ "syn 2.0.106",
]
[[package]]
@@ -6451,7 +6451,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4b0f83760fb341a774ed326568e19f5a863af4a952def8c39f9ab92fd95b88e5"
dependencies = [
"equivalent",
- "hashbrown 0.15.5",
+ "hashbrown 0.16.0",
"serde",
"serde_core",
]
@@ -8663,7 +8663,7 @@ version = "0.7.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "77e878c846a8abae00dd069496dbe8751b16ac1c3d6bd2a7283a938e8228f90d"
dependencies = [
- "proc-macro-crate 1.1.3",
+ "proc-macro-crate 3.4.0",
"proc-macro2",
"quote",
"syn 2.0.106",
From 70031a4883f84deabf9276d2bb12a4e8237d4adb Mon Sep 17 00:00:00 2001
From: Niki Yerm
Date: Sat, 7 Feb 2026 19:08:03 +0100
Subject: [PATCH 13/16] cargo fmt
---
pallets/subtensor/src/lib.rs | 17 ++++++++---------
1 file changed, 8 insertions(+), 9 deletions(-)
diff --git a/pallets/subtensor/src/lib.rs b/pallets/subtensor/src/lib.rs
index 6d6205a64b..3f93981a8c 100644
--- a/pallets/subtensor/src/lib.rs
+++ b/pallets/subtensor/src/lib.rs
@@ -79,17 +79,17 @@ pub const MAX_ROOT_CLAIM_THRESHOLD: u64 = 10_000_000;
#[frame_support::pallet]
#[allow(clippy::expect_used)]
pub mod pallet {
+ use crate::RateLimitKey;
use crate::migrations;
use crate::subnets::leasing::{LeaseId, SubnetLeaseOf};
- use crate::RateLimitKey;
use frame_support::Twox64Concat;
use frame_support::{
+ BoundedVec,
dispatch::GetDispatchInfo,
pallet_prelude::{DispatchResult, StorageMap, ValueQuery, *},
traits::{
- tokens::fungible, OriginTrait, QueryPreimage, StorePreimage, UnfilteredDispatchable,
+ OriginTrait, QueryPreimage, StorePreimage, UnfilteredDispatchable, tokens::fungible,
},
- BoundedVec,
};
use frame_system::pallet_prelude::*;
use pallet_drand::types::RoundNumber;
@@ -1942,7 +1942,6 @@ pub mod pallet {
#[pallet::storage]
/// --- MAP ( netuid ) --> If subtoken trading enabled
- #[pallet::storage]
pub type SubtokenEnabled =
StorageMap<_, Identity, NetUid, bool, ValueQuery, DefaultFalse>;
@@ -1955,10 +1954,10 @@ pub mod pallet {
/// alpha = 0.1 means slow response, 10% weight to new values per epoch
pub fn DefaultVotingPowerEmaAlpha() -> u64 {
0_003_570_000_000_000_000 // 0.00357 * 10^18 = 2 weeks e-folding (time-constant) @ 361
- // blocks per tempo
- // After 2 weeks -> EMA reaches 63.2% of a step change
- // After ~4 weeks -> 86.5%
- // After ~6 weeks -> 95%
+ // blocks per tempo
+ // After 2 weeks -> EMA reaches 63.2% of a step change
+ // After ~4 weeks -> 86.5%
+ // After ~6 weeks -> 95%
}
#[pallet::storage]
@@ -2727,7 +2726,7 @@ pub enum RateLimitKey {
pub trait ProxyInterface {
fn add_lease_beneficiary_proxy(beneficiary: &AccountId, lease: &AccountId) -> DispatchResult;
fn remove_lease_beneficiary_proxy(beneficiary: &AccountId, lease: &AccountId)
- -> DispatchResult;
+ -> DispatchResult;
}
impl ProxyInterface for () {
From abda84aeb0dc76bb4cbc64ba8395a9065e12a483 Mon Sep 17 00:00:00 2001
From: Niki Yerm
Date: Sun, 8 Feb 2026 12:09:32 +0100
Subject: [PATCH 14/16] Address PR review feedback for consensus modes
- Extract duplicated consensus u16->I32F32 conversion to helper function
- Replace manual diff assertions with assert_abs_diff_eq! macro
- Add dispatchable extrinsic for set_liquid_alpha_consensus_mode
---
pallets/subtensor/src/epoch/run_epoch.rs | 38 +++++++++----------
pallets/subtensor/src/lib.rs | 4 +-
pallets/subtensor/src/macros/dispatches.rs | 31 +++++++++++++++
pallets/subtensor/src/tests/consensus_mode.rs | 32 ++++++----------
pallets/subtensor/src/tests/epoch.rs | 5 ++-
pallets/subtensor/src/utils/misc.rs | 2 +-
6 files changed, 68 insertions(+), 44 deletions(-)
diff --git a/pallets/subtensor/src/epoch/run_epoch.rs b/pallets/subtensor/src/epoch/run_epoch.rs
index 041dc77c4f..e4069bd452 100644
--- a/pallets/subtensor/src/epoch/run_epoch.rs
+++ b/pallets/subtensor/src/epoch/run_epoch.rs
@@ -1377,34 +1377,30 @@ impl Pallet {
}
ConsensusMode::Previous => {
// Use consensus from storage
- let previous_consensus_u16 = Consensus::::get(netuid);
- previous_consensus_u16
- .iter()
- .map(|&c| {
- I32F32::saturating_from_num(c)
- .safe_div(I32F32::saturating_from_num(u16::MAX))
- })
- .collect()
+ Self::get_previous_consensus_as_i32f32(netuid)
}
ConsensusMode::Auto => {
// Auto mode: Previous if bond_penalty == 1, otherwise Current
let bonds_penalty = Self::get_float_bonds_penalty(netuid);
- if bonds_penalty == I32F32::from_num(1) {
- let previous_consensus_u16 = Consensus::::get(netuid);
- previous_consensus_u16
- .iter()
- .map(|&c| {
- I32F32::saturating_from_num(c)
- .safe_div(I32F32::saturating_from_num(u16::MAX))
- })
- .collect()
- } else {
- current_consensus.to_vec()
- }
+ (bonds_penalty == I32F32::from_num(1))
+ .then(|| Self::get_previous_consensus_as_i32f32(netuid))
+ .unwrap_or_else(|| current_consensus.to_vec())
}
}
}
+ /// Convert stored consensus (u16 values) to I32F32 format
+ /// Used by consensus modes that need to read from storage
+ fn get_previous_consensus_as_i32f32(netuid: NetUid) -> Vec {
+ let previous_consensus_u16 = Consensus::::get(netuid);
+ previous_consensus_u16
+ .iter()
+ .map(|&c| {
+ I32F32::saturating_from_num(c).safe_div(I32F32::saturating_from_num(u16::MAX))
+ })
+ .collect()
+ }
+
/// Compute liquid alphas matrix
/// There is a separate alpha param for each validator-miner binding
///
@@ -1619,7 +1615,7 @@ impl Pallet {
) -> Result<(), DispatchError> {
Self::ensure_subnet_owner_or_root(origin, netuid)?;
- Self::set_liquid_alpha_consensus_mode(netuid, mode.clone());
+ Self::set_liquid_alpha_consensus_mode_storage(netuid, mode.clone());
log::debug!("LiquidAlphaConsensusModeSet( netuid: {netuid:?}, mode: {mode:?} )",);
Ok(())
diff --git a/pallets/subtensor/src/lib.rs b/pallets/subtensor/src/lib.rs
index 3f93981a8c..7bd6c13dfd 100644
--- a/pallets/subtensor/src/lib.rs
+++ b/pallets/subtensor/src/lib.rs
@@ -349,7 +349,9 @@ pub mod pallet {
}
/// Enum for consensus mode used in liquid alpha calculation
- #[derive(Encode, Decode, Default, TypeInfo, Clone, PartialEq, Eq, Debug)]
+ #[derive(
+ Encode, Decode, DecodeWithMemTracking, Default, TypeInfo, Clone, PartialEq, Eq, Debug,
+ )]
pub enum ConsensusMode {
/// Use current in-memory consensus (current behavior)
Current,
diff --git a/pallets/subtensor/src/macros/dispatches.rs b/pallets/subtensor/src/macros/dispatches.rs
index f70b83f52d..7287c6ddab 100644
--- a/pallets/subtensor/src/macros/dispatches.rs
+++ b/pallets/subtensor/src/macros/dispatches.rs
@@ -2626,5 +2626,36 @@ mod dispatches {
Self::deposit_event(Event::ColdkeySwapCleared { who });
Ok(())
}
+
+ /// Sets the consensus mode for liquid alpha calculation on a subnet.
+ ///
+ /// This function can only be called by the subnet owner or root.
+ /// The consensus mode determines which consensus values are used for liquid alpha calculation:
+ /// - `Current`: Use current in-memory consensus
+ /// - `Previous`: Use previous consensus from storage
+ /// - `Auto`: Use Previous if bond_penalty == 1, otherwise Current (default)
+ ///
+ /// # Arguments:
+ /// * `origin` - The origin of the call, must be subnet owner or root.
+ /// * `netuid` - The subnet to set the mode for.
+ /// * `mode` - The consensus mode to use.
+ ///
+ /// # Errors:
+ /// * `BadOrigin` - If the origin is not the subnet owner or root.
+ #[pallet::call_index(133)]
+ #[pallet::weight((
+ Weight::from_parts(10_000, 0)
+ .saturating_add(T::DbWeight::get().reads(2))
+ .saturating_add(T::DbWeight::get().writes(1)),
+ DispatchClass::Operational,
+ Pays::Yes
+ ))]
+ pub fn set_liquid_alpha_consensus_mode(
+ origin: OriginFor,
+ netuid: NetUid,
+ mode: ConsensusMode,
+ ) -> DispatchResult {
+ Self::do_set_liquid_alpha_consensus_mode(origin, netuid, mode)
+ }
}
}
diff --git a/pallets/subtensor/src/tests/consensus_mode.rs b/pallets/subtensor/src/tests/consensus_mode.rs
index e9aeec6368..8bdd7a2c67 100644
--- a/pallets/subtensor/src/tests/consensus_mode.rs
+++ b/pallets/subtensor/src/tests/consensus_mode.rs
@@ -1,3 +1,4 @@
+use approx::assert_abs_diff_eq;
use frame_support::{assert_err, assert_ok};
use sp_core::U256;
use substrate_fixed::types::I32F32;
@@ -175,7 +176,7 @@ fn test_compute_consensus_current_mode() {
// Setup network and test data
setup_consensus_test_environment(netuid);
- SubtensorModule::set_liquid_alpha_consensus_mode(netuid, ConsensusMode::Current);
+ SubtensorModule::set_liquid_alpha_consensus_mode_storage(netuid, ConsensusMode::Current);
let (current_consensus, _previous_values) = create_test_consensus_data(netuid);
// Compute consensus for liquid alpha
@@ -199,7 +200,7 @@ fn test_compute_consensus_previous_mode() {
// Setup network and test data
setup_consensus_test_environment(netuid);
- SubtensorModule::set_liquid_alpha_consensus_mode(netuid, ConsensusMode::Previous);
+ SubtensorModule::set_liquid_alpha_consensus_mode_storage(netuid, ConsensusMode::Previous);
let (current_consensus, previous_values) = create_test_consensus_data(netuid);
// Compute consensus for liquid alpha
@@ -210,15 +211,10 @@ fn test_compute_consensus_previous_mode() {
assert_eq!(result.len(), n);
for (res, &prev) in result.iter().zip(previous_values.iter()) {
let expected = I32F32::from_num(prev);
- // Allow small floating point difference
- let diff = if *res > expected {
- *res - expected
- } else {
- expected - *res
- };
- assert!(
- diff < I32F32::from_num(0.001),
- "Values should be approximately equal"
+ assert_abs_diff_eq!(
+ res.to_num::(),
+ expected.to_num::(),
+ epsilon = 0.001
);
}
});
@@ -233,7 +229,7 @@ fn test_compute_consensus_auto_mode() {
// Setup network and test data
setup_consensus_test_environment(netuid);
- SubtensorModule::set_liquid_alpha_consensus_mode(netuid, ConsensusMode::Auto);
+ SubtensorModule::set_liquid_alpha_consensus_mode_storage(netuid, ConsensusMode::Auto);
let (current_consensus, previous_values) = create_test_consensus_data(netuid);
// Test 1: bond_penalty != 1, should use Current
@@ -257,14 +253,10 @@ fn test_compute_consensus_auto_mode() {
assert_eq!(result.len(), n);
for (res, &prev) in result.iter().zip(previous_values.iter()) {
let expected = I32F32::from_num(prev);
- let diff = if *res > expected {
- *res - expected
- } else {
- expected - *res
- };
- assert!(
- diff < I32F32::from_num(0.001),
- "Should use previous consensus when bond_penalty == 1"
+ assert_abs_diff_eq!(
+ res.to_num::(),
+ expected.to_num::(),
+ epsilon = 0.001
);
}
});
diff --git a/pallets/subtensor/src/tests/epoch.rs b/pallets/subtensor/src/tests/epoch.rs
index db32f0c7b0..c6676c80ac 100644
--- a/pallets/subtensor/src/tests/epoch.rs
+++ b/pallets/subtensor/src/tests/epoch.rs
@@ -3556,7 +3556,10 @@ fn test_liquid_alpha_equal_values_against_itself() {
// compute bonds with liquid alpha enabled
SubtensorModule::set_liquid_alpha_enabled(netuid.into(), true);
// Set consensus mode to Current to match the original behavior (using in-memory consensus)
- SubtensorModule::set_liquid_alpha_consensus_mode(netuid.into(), ConsensusMode::Current);
+ SubtensorModule::set_liquid_alpha_consensus_mode_storage(
+ netuid.into(),
+ ConsensusMode::Current,
+ );
let new_bonds_liquid_alpha_on =
SubtensorModule::compute_bonds(netuid.into(), &weights, &bonds, &consensus);
diff --git a/pallets/subtensor/src/utils/misc.rs b/pallets/subtensor/src/utils/misc.rs
index 68bce62b9a..47f2ab26cf 100644
--- a/pallets/subtensor/src/utils/misc.rs
+++ b/pallets/subtensor/src/utils/misc.rs
@@ -780,7 +780,7 @@ impl Pallet {
LiquidAlphaConsensusMode::::get(netuid)
}
- pub fn set_liquid_alpha_consensus_mode(netuid: NetUid, mode: ConsensusMode) {
+ pub fn set_liquid_alpha_consensus_mode_storage(netuid: NetUid, mode: ConsensusMode) {
LiquidAlphaConsensusMode::::insert(netuid, mode);
}
From 5701e8c4087aa0db7b1dad9b7c42cabdb20b5fa5 Mon Sep 17 00:00:00 2001
From: konrad0960
Date: Tue, 10 Feb 2026 00:15:54 +0100
Subject: [PATCH 15/16] Address PR review: DispatchClass::Normal and clippy fix
---
pallets/subtensor/src/epoch/run_epoch.rs | 8 +++++---
pallets/subtensor/src/macros/dispatches.rs | 2 +-
2 files changed, 6 insertions(+), 4 deletions(-)
diff --git a/pallets/subtensor/src/epoch/run_epoch.rs b/pallets/subtensor/src/epoch/run_epoch.rs
index e4069bd452..da18e39776 100644
--- a/pallets/subtensor/src/epoch/run_epoch.rs
+++ b/pallets/subtensor/src/epoch/run_epoch.rs
@@ -1382,9 +1382,11 @@ impl Pallet {
ConsensusMode::Auto => {
// Auto mode: Previous if bond_penalty == 1, otherwise Current
let bonds_penalty = Self::get_float_bonds_penalty(netuid);
- (bonds_penalty == I32F32::from_num(1))
- .then(|| Self::get_previous_consensus_as_i32f32(netuid))
- .unwrap_or_else(|| current_consensus.to_vec())
+ if bonds_penalty == I32F32::from_num(1) {
+ Self::get_previous_consensus_as_i32f32(netuid)
+ } else {
+ current_consensus.to_vec()
+ }
}
}
}
diff --git a/pallets/subtensor/src/macros/dispatches.rs b/pallets/subtensor/src/macros/dispatches.rs
index 7287c6ddab..beb0d41ec0 100644
--- a/pallets/subtensor/src/macros/dispatches.rs
+++ b/pallets/subtensor/src/macros/dispatches.rs
@@ -2647,7 +2647,7 @@ mod dispatches {
Weight::from_parts(10_000, 0)
.saturating_add(T::DbWeight::get().reads(2))
.saturating_add(T::DbWeight::get().writes(1)),
- DispatchClass::Operational,
+ DispatchClass::Normal,
Pays::Yes
))]
pub fn set_liquid_alpha_consensus_mode(
From 85fa05599ff22d32c2e56cfac0209e5f6a253503 Mon Sep 17 00:00:00 2001
From: Pawel Polewicz
Date: Sun, 5 Apr 2026 08:05:09 +0000
Subject: [PATCH 16/16] Address remaining consensus mode review cleanup
---
pallets/subtensor/src/epoch/run_epoch.rs | 6 +-
pallets/subtensor/src/macros/dispatches.rs | 2 +-
pallets/subtensor/src/tests/consensus_mode.rs | 105 ++++--------------
pallets/subtensor/src/tests/epoch.rs | 5 +-
pallets/subtensor/src/tests/mock.rs | 52 +++++++++
pallets/subtensor/src/utils/misc.rs | 8 --
6 files changed, 78 insertions(+), 100 deletions(-)
diff --git a/pallets/subtensor/src/epoch/run_epoch.rs b/pallets/subtensor/src/epoch/run_epoch.rs
index da18e39776..c0c9239e46 100644
--- a/pallets/subtensor/src/epoch/run_epoch.rs
+++ b/pallets/subtensor/src/epoch/run_epoch.rs
@@ -1368,7 +1368,7 @@ impl Pallet {
netuid: NetUid,
current_consensus: &[I32F32],
) -> Vec {
- let mode = Self::get_liquid_alpha_consensus_mode(netuid);
+ let mode = LiquidAlphaConsensusMode::::get(netuid);
match mode {
ConsensusMode::Current => {
@@ -1611,13 +1611,13 @@ impl Pallet {
}
pub fn do_set_liquid_alpha_consensus_mode(
- origin: T::RuntimeOrigin,
+ origin: OriginFor,
netuid: NetUid,
mode: ConsensusMode,
) -> Result<(), DispatchError> {
Self::ensure_subnet_owner_or_root(origin, netuid)?;
- Self::set_liquid_alpha_consensus_mode_storage(netuid, mode.clone());
+ LiquidAlphaConsensusMode::::insert(netuid, mode.clone());
log::debug!("LiquidAlphaConsensusModeSet( netuid: {netuid:?}, mode: {mode:?} )",);
Ok(())
diff --git a/pallets/subtensor/src/macros/dispatches.rs b/pallets/subtensor/src/macros/dispatches.rs
index beb0d41ec0..8ab8e9c6c3 100644
--- a/pallets/subtensor/src/macros/dispatches.rs
+++ b/pallets/subtensor/src/macros/dispatches.rs
@@ -2642,7 +2642,7 @@ mod dispatches {
///
/// # Errors:
/// * `BadOrigin` - If the origin is not the subnet owner or root.
- #[pallet::call_index(133)]
+ #[pallet::call_index(134)]
#[pallet::weight((
Weight::from_parts(10_000, 0)
.saturating_add(T::DbWeight::get().reads(2))
diff --git a/pallets/subtensor/src/tests/consensus_mode.rs b/pallets/subtensor/src/tests/consensus_mode.rs
index 8bdd7a2c67..dff7f9c747 100644
--- a/pallets/subtensor/src/tests/consensus_mode.rs
+++ b/pallets/subtensor/src/tests/consensus_mode.rs
@@ -1,64 +1,11 @@
use approx::assert_abs_diff_eq;
use frame_support::{assert_err, assert_ok};
use sp_core::U256;
-use substrate_fixed::types::I32F32;
use subtensor_runtime_common::NetUid;
-use super::consensus::{fixed, fixed_proportion_to_u16};
use super::mock::*;
use crate::*;
-// ============================================================================
-// Test Helper Functions
-// ============================================================================
-
-/// Sets up a network with full owner permissions (root registration + network creation)
-/// Returns (netuid, hotkey, coldkey, signer)
-fn setup_network_with_owner() -> (NetUid, U256, U256, RuntimeOrigin) {
- let hotkey = U256::from(1);
- let coldkey = U256::from(457);
- let netuid = add_dynamic_network(&hotkey, &coldkey);
- let signer = RuntimeOrigin::signed(coldkey);
-
- migrations::migrate_create_root_network::migrate_create_root_network::();
- SubtensorModule::add_balance_to_coldkey_account(&coldkey, 1_000_000_000_000_000);
- assert_ok!(SubtensorModule::root_register(signer.clone(), hotkey));
- assert_ok!(SubtensorModule::register_network(signer.clone(), hotkey));
-
- (netuid, hotkey, coldkey, signer)
-}
-
-/// Sets up a basic network environment for consensus testing
-/// Creates network and enables liquid alpha
-fn setup_consensus_test_environment(netuid: NetUid) {
- add_network(netuid, 0, 0);
- SubtensorModule::set_liquid_alpha_enabled(netuid, true);
-}
-
-/// Creates test consensus data and stores it in the system
-/// Returns (current_consensus, previous_values)
-fn create_test_consensus_data(netuid: NetUid) -> (Vec, Vec) {
- let current_consensus: Vec = vec![
- I32F32::from_num(0.2),
- I32F32::from_num(0.3),
- I32F32::from_num(0.4),
- I32F32::from_num(0.1),
- ];
-
- let previous_values: Vec = vec![0.1, 0.2, 0.3, 0.4];
- let previous_consensus_u16: Vec = previous_values
- .iter()
- .map(|&v| fixed_proportion_to_u16(fixed(v)))
- .collect();
- Consensus::::insert(netuid, previous_consensus_u16);
-
- (current_consensus, previous_values)
-}
-
-// ============================================================================
-// Tests
-// ============================================================================
-
/// Test setting consensus mode when liquid alpha is disabled
#[test]
fn test_set_consensus_mode_liquid_alpha_disabled() {
@@ -69,7 +16,7 @@ fn test_set_consensus_mode_liquid_alpha_disabled() {
assert!(!SubtensorModule::get_liquid_alpha_enabled(netuid));
// Should succeed to set consensus mode even when liquid alpha is disabled
- assert_ok!(SubtensorModule::do_set_liquid_alpha_consensus_mode(
+ assert_ok!(SubtensorModule::set_liquid_alpha_consensus_mode(
signer.clone(),
netuid,
ConsensusMode::Previous
@@ -77,7 +24,7 @@ fn test_set_consensus_mode_liquid_alpha_disabled() {
// Verify the mode was set
assert_eq!(
- SubtensorModule::get_liquid_alpha_consensus_mode(netuid),
+ LiquidAlphaConsensusMode::::get(netuid),
ConsensusMode::Previous
);
@@ -85,7 +32,7 @@ fn test_set_consensus_mode_liquid_alpha_disabled() {
SubtensorModule::set_liquid_alpha_enabled(netuid, true);
// Should still succeed
- assert_ok!(SubtensorModule::do_set_liquid_alpha_consensus_mode(
+ assert_ok!(SubtensorModule::set_liquid_alpha_consensus_mode(
signer.clone(),
netuid,
ConsensusMode::Current
@@ -93,7 +40,7 @@ fn test_set_consensus_mode_liquid_alpha_disabled() {
// Verify the mode was updated
assert_eq!(
- SubtensorModule::get_liquid_alpha_consensus_mode(netuid),
+ LiquidAlphaConsensusMode::::get(netuid),
ConsensusMode::Current
);
});
@@ -109,7 +56,7 @@ fn test_set_consensus_mode_permissions() {
// Non-owner should fail
assert_err!(
- SubtensorModule::do_set_liquid_alpha_consensus_mode(
+ SubtensorModule::set_liquid_alpha_consensus_mode(
non_owner_signer,
netuid,
ConsensusMode::Previous
@@ -118,7 +65,7 @@ fn test_set_consensus_mode_permissions() {
);
// Owner should succeed
- assert_ok!(SubtensorModule::do_set_liquid_alpha_consensus_mode(
+ assert_ok!(SubtensorModule::set_liquid_alpha_consensus_mode(
owner_signer,
netuid,
ConsensusMode::Previous
@@ -133,35 +80,35 @@ fn test_set_and_get_consensus_modes() {
let (netuid, _hotkey, _coldkey, signer) = setup_network_with_owner();
// Test Current mode
- assert_ok!(SubtensorModule::do_set_liquid_alpha_consensus_mode(
+ assert_ok!(SubtensorModule::set_liquid_alpha_consensus_mode(
signer.clone(),
netuid,
ConsensusMode::Current
));
assert_eq!(
- SubtensorModule::get_liquid_alpha_consensus_mode(netuid),
+ LiquidAlphaConsensusMode::::get(netuid),
ConsensusMode::Current
);
// Test Previous mode
- assert_ok!(SubtensorModule::do_set_liquid_alpha_consensus_mode(
+ assert_ok!(SubtensorModule::set_liquid_alpha_consensus_mode(
signer.clone(),
netuid,
ConsensusMode::Previous
));
assert_eq!(
- SubtensorModule::get_liquid_alpha_consensus_mode(netuid),
+ LiquidAlphaConsensusMode::::get(netuid),
ConsensusMode::Previous
);
// Test Auto mode
- assert_ok!(SubtensorModule::do_set_liquid_alpha_consensus_mode(
+ assert_ok!(SubtensorModule::set_liquid_alpha_consensus_mode(
signer.clone(),
netuid,
ConsensusMode::Auto
));
assert_eq!(
- SubtensorModule::get_liquid_alpha_consensus_mode(netuid),
+ LiquidAlphaConsensusMode::::get(netuid),
ConsensusMode::Auto
);
});
@@ -176,7 +123,7 @@ fn test_compute_consensus_current_mode() {
// Setup network and test data
setup_consensus_test_environment(netuid);
- SubtensorModule::set_liquid_alpha_consensus_mode_storage(netuid, ConsensusMode::Current);
+ LiquidAlphaConsensusMode::::insert(netuid, ConsensusMode::Current);
let (current_consensus, _previous_values) = create_test_consensus_data(netuid);
// Compute consensus for liquid alpha
@@ -200,8 +147,8 @@ fn test_compute_consensus_previous_mode() {
// Setup network and test data
setup_consensus_test_environment(netuid);
- SubtensorModule::set_liquid_alpha_consensus_mode_storage(netuid, ConsensusMode::Previous);
- let (current_consensus, previous_values) = create_test_consensus_data(netuid);
+ LiquidAlphaConsensusMode::::insert(netuid, ConsensusMode::Previous);
+ let (current_consensus, previous_consensus) = create_test_consensus_data(netuid);
// Compute consensus for liquid alpha
let result =
@@ -209,13 +156,8 @@ fn test_compute_consensus_previous_mode() {
// Should return previous consensus from storage (not current)
assert_eq!(result.len(), n);
- for (res, &prev) in result.iter().zip(previous_values.iter()) {
- let expected = I32F32::from_num(prev);
- assert_abs_diff_eq!(
- res.to_num::(),
- expected.to_num::(),
- epsilon = 0.001
- );
+ for (res, prev) in result.iter().zip(previous_consensus.iter()) {
+ assert_abs_diff_eq!(res.to_num::(), prev.to_num::(), epsilon = 0.001);
}
});
}
@@ -229,8 +171,8 @@ fn test_compute_consensus_auto_mode() {
// Setup network and test data
setup_consensus_test_environment(netuid);
- SubtensorModule::set_liquid_alpha_consensus_mode_storage(netuid, ConsensusMode::Auto);
- let (current_consensus, previous_values) = create_test_consensus_data(netuid);
+ LiquidAlphaConsensusMode::::insert(netuid, ConsensusMode::Auto);
+ let (current_consensus, previous_consensus) = create_test_consensus_data(netuid);
// Test 1: bond_penalty != 1, should use Current
SubtensorModule::set_bonds_penalty(netuid, u16::MAX / 2); // 0.5
@@ -251,13 +193,8 @@ fn test_compute_consensus_auto_mode() {
SubtensorModule::compute_consensus_for_liquid_alpha(netuid, ¤t_consensus);
assert_eq!(result.len(), n);
- for (res, &prev) in result.iter().zip(previous_values.iter()) {
- let expected = I32F32::from_num(prev);
- assert_abs_diff_eq!(
- res.to_num::(),
- expected.to_num::(),
- epsilon = 0.001
- );
+ for (res, prev) in result.iter().zip(previous_consensus.iter()) {
+ assert_abs_diff_eq!(res.to_num::(), prev.to_num::(), epsilon = 0.001);
}
});
}
diff --git a/pallets/subtensor/src/tests/epoch.rs b/pallets/subtensor/src/tests/epoch.rs
index c6676c80ac..d0b47c4c54 100644
--- a/pallets/subtensor/src/tests/epoch.rs
+++ b/pallets/subtensor/src/tests/epoch.rs
@@ -3556,10 +3556,7 @@ fn test_liquid_alpha_equal_values_against_itself() {
// compute bonds with liquid alpha enabled
SubtensorModule::set_liquid_alpha_enabled(netuid.into(), true);
// Set consensus mode to Current to match the original behavior (using in-memory consensus)
- SubtensorModule::set_liquid_alpha_consensus_mode_storage(
- netuid.into(),
- ConsensusMode::Current,
- );
+ LiquidAlphaConsensusMode::::insert(netuid, ConsensusMode::Current);
let new_bonds_liquid_alpha_on =
SubtensorModule::compute_bonds(netuid.into(), &weights, &bonds, &consensus);
diff --git a/pallets/subtensor/src/tests/mock.rs b/pallets/subtensor/src/tests/mock.rs
index 772909638b..5c41877b39 100644
--- a/pallets/subtensor/src/tests/mock.rs
+++ b/pallets/subtensor/src/tests/mock.rs
@@ -29,6 +29,7 @@ use sp_runtime::{
};
use sp_std::{cell::RefCell, cmp::Ordering, sync::OnceLock};
use sp_tracing::tracing_subscriber;
+use substrate_fixed::types::I32F32;
use subtensor_runtime_common::{AuthorshipInfo, NetUid, TaoBalance};
use subtensor_swap_interface::{Order, SwapHandler};
use tracing_subscriber::{EnvFilter, layer::SubscriberExt, util::SubscriberInitExt};
@@ -823,6 +824,57 @@ pub fn add_dynamic_network_disable_commit_reveal(hotkey: &U256, coldkey: &U256)
netuid
}
+#[allow(dead_code)]
+pub fn setup_network_with_owner() -> (NetUid, U256, U256, RuntimeOrigin) {
+ let hotkey = U256::from(1);
+ let coldkey = U256::from(457);
+ let netuid = add_dynamic_network(&hotkey, &coldkey);
+ let signer = RuntimeOrigin::signed(coldkey);
+
+ migrations::migrate_create_root_network::migrate_create_root_network::();
+ SubtensorModule::add_balance_to_coldkey_account(
+ &coldkey,
+ TaoBalance::from(1_000_000_000_000_000u64),
+ );
+ assert_ok!(SubtensorModule::root_register(signer.clone(), hotkey));
+ assert_ok!(SubtensorModule::register_network(signer.clone(), hotkey));
+
+ (netuid, hotkey, coldkey, signer)
+}
+
+#[allow(dead_code)]
+pub fn setup_consensus_test_environment(netuid: NetUid) {
+ add_network(netuid, 0, 0);
+ SubtensorModule::set_liquid_alpha_enabled(netuid, true);
+}
+
+#[allow(dead_code)]
+pub fn create_test_consensus_data(netuid: NetUid) -> (Vec, Vec) {
+ let current_consensus = vec![
+ I32F32::from_num(0.2),
+ I32F32::from_num(0.3),
+ I32F32::from_num(0.4),
+ I32F32::from_num(0.1),
+ ];
+ let previous_consensus = vec![
+ I32F32::from_num(0.1),
+ I32F32::from_num(0.2),
+ I32F32::from_num(0.3),
+ I32F32::from_num(0.4),
+ ];
+ let previous_consensus_u16: Vec = previous_consensus
+ .iter()
+ .map(|value| {
+ value
+ .saturating_mul(I32F32::saturating_from_num(u16::MAX))
+ .saturating_to_num::()
+ })
+ .collect();
+ Consensus::::insert(netuid, previous_consensus_u16);
+
+ (current_consensus, previous_consensus)
+}
+
#[allow(dead_code)]
pub fn add_network_disable_commit_reveal(netuid: NetUid, tempo: u16, _modality: u16) {
add_network(netuid, tempo, _modality);
diff --git a/pallets/subtensor/src/utils/misc.rs b/pallets/subtensor/src/utils/misc.rs
index 47f2ab26cf..45462ec600 100644
--- a/pallets/subtensor/src/utils/misc.rs
+++ b/pallets/subtensor/src/utils/misc.rs
@@ -776,14 +776,6 @@ impl Pallet {
(converted_low, converted_high)
}
- pub fn get_liquid_alpha_consensus_mode(netuid: NetUid) -> ConsensusMode {
- LiquidAlphaConsensusMode::::get(netuid)
- }
-
- pub fn set_liquid_alpha_consensus_mode_storage(netuid: NetUid, mode: ConsensusMode) {
- LiquidAlphaConsensusMode::::insert(netuid, mode);
- }
-
pub fn set_alpha_sigmoid_steepness(netuid: NetUid, steepness: i16) {
AlphaSigmoidSteepness::::insert(netuid, steepness);
}