diff --git a/.claude/skills/fix/SKILL.md b/.claude/skills/fix/SKILL.md new file mode 100644 index 0000000000..d097f3fbfe --- /dev/null +++ b/.claude/skills/fix/SKILL.md @@ -0,0 +1,33 @@ +--- +name: fix +description: Commit current changes, run Rust autofix/lint/format, run pallet-subtensor tests, amend with any fixes. +--- + +# Fix Skill + +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 + +1. Run /format +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) + - 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 + +- 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..9a0424b28b --- /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 (unless there are none) +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 diff --git a/.claude/skills/ship/SKILL.md b/.claude/skills/ship/SKILL.md new file mode 100644 index 0000000000..163015b8b2 --- /dev/null +++ b/.claude/skills/ship/SKILL.md @@ -0,0 +1,77 @@ +--- +name: ship +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 branch through CI and review without force-pushes, and never apply review fixes without explicit user approval. + +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` +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 ""). + - **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` +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. + - 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. +8. **Once CI is green** (or only ignored checks are failing), perform a thorough code review. + - **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**, 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? + - 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. + +10. **Report to user**: Present a formatted summary: + ``` + ## Code Review Results + + ### R-1: [severity] + **File**: path/to/file.rs:42 + **Issue**: <description> + **Proposed fix**: <summary of fix> + **Review**: Approved / Rejected — <reasoning> + + ### 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, 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. diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000000..2a40a8e0d8 --- /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 or attribution in PRs diff --git a/pallets/subtensor/src/epoch/run_epoch.rs b/pallets/subtensor/src/epoch/run_epoch.rs index 793f6fbe4a..c0c9239e46 100644 --- a/pallets/subtensor/src/epoch/run_epoch.rs +++ b/pallets/subtensor/src/epoch/run_epoch.rs @@ -1274,9 +1274,21 @@ impl<T: Config> Pallet<T> { .iter() .any(|&c| c != I32F32::saturating_from_num(0)) { - // Liquid Alpha is enabled, compute the liquid alphas matrix. - let alphas: Vec<Vec<I32F32>> = - Self::compute_liquid_alpha_values(netuid, weights, bonds, consensus); + // 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<Vec<I32F32>> = 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 +1328,21 @@ impl<T: Config> Pallet<T> { .iter() .any(|&c| c != I32F32::saturating_from_num(0)) { - // Liquid Alpha is enabled, compute the liquid alphas matrix. - let alphas: Vec<Vec<I32F32>> = - Self::compute_liquid_alpha_values_sparse(netuid, weights, bonds, consensus); + // 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<Vec<I32F32>> = 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 +1356,53 @@ impl<T: Config> Pallet<T> { } } + /// 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<I32F32> { + let mode = LiquidAlphaConsensusMode::<T>::get(netuid); + + match mode { + ConsensusMode::Current => { + // Use the in-memory consensus (current behavior) + current_consensus.to_vec() + } + ConsensusMode::Previous => { + // Use consensus from storage + 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) { + Self::get_previous_consensus_as_i32f32(netuid) + } 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<I32F32> { + let previous_consensus_u16 = Consensus::<T>::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 /// @@ -1539,6 +1610,19 @@ impl<T: Config> Pallet<T> { Ok(()) } + pub fn do_set_liquid_alpha_consensus_mode( + origin: OriginFor<T>, + netuid: NetUid, + mode: ConsensusMode, + ) -> Result<(), DispatchError> { + Self::ensure_subnet_owner_or_root(origin, netuid)?; + + LiquidAlphaConsensusMode::<T>::insert(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..7bd6c13dfd 100644 --- a/pallets/subtensor/src/lib.rs +++ b/pallets/subtensor/src/lib.rs @@ -348,6 +348,19 @@ pub mod pallet { }, } + /// Enum for consensus mode used in liquid alpha calculation + #[derive( + Encode, Decode, DecodeWithMemTracking, Default, TypeInfo, Clone, PartialEq, Eq, Debug, + )] + pub enum ConsensusMode { + /// Use current in-memory consensus (current behavior) + Current, + /// Use previous consensus from storage + Previous, + /// 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<T: Config>() -> ConsensusMode { + ConsensusMode::default() + } + /// Default value for coldkey swap announcement delay. #[pallet::type_value] pub fn DefaultColdkeySwapAnnouncementDelay<T: Config>() -> BlockNumberFor<T> { @@ -1918,8 +1937,13 @@ pub mod pallet { pub type AlphaValues<T> = StorageMap<_, Identity, NetUid, (u16, u16), ValueQuery, DefaultAlphaValues<T>>; - /// --- MAP ( netuid ) --> If subtoken trading enabled #[pallet::storage] + /// MAP ( netuid ) --> consensus mode for liquid alpha calculation + pub type LiquidAlphaConsensusMode<T> = + StorageMap<_, Identity, NetUid, ConsensusMode, ValueQuery, DefaultConsensusMode<T>>; + + #[pallet::storage] + /// --- MAP ( netuid ) --> If subtoken trading enabled pub type SubtokenEnabled<T> = StorageMap<_, Identity, NetUid, bool, ValueQuery, DefaultFalse<T>>; diff --git a/pallets/subtensor/src/macros/dispatches.rs b/pallets/subtensor/src/macros/dispatches.rs index f70b83f52d..8ab8e9c6c3 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(134)] + #[pallet::weight(( + Weight::from_parts(10_000, 0) + .saturating_add(T::DbWeight::get().reads(2)) + .saturating_add(T::DbWeight::get().writes(1)), + DispatchClass::Normal, + Pays::Yes + ))] + pub fn set_liquid_alpha_consensus_mode( + origin: OriginFor<T>, + 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 new file mode 100644 index 0000000000..dff7f9c747 --- /dev/null +++ b/pallets/subtensor/src/tests/consensus_mode.rs @@ -0,0 +1,200 @@ +use approx::assert_abs_diff_eq; +use frame_support::{assert_err, assert_ok}; +use sp_core::U256; +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 (netuid, _hotkey, _coldkey, signer) = setup_network_with_owner(); + + // Liquid Alpha is disabled by default + assert!(!SubtensorModule::get_liquid_alpha_enabled(netuid)); + + // Should succeed to set consensus mode even when liquid alpha is disabled + assert_ok!(SubtensorModule::set_liquid_alpha_consensus_mode( + signer.clone(), + netuid, + ConsensusMode::Previous + )); + + // Verify the mode was set + assert_eq!( + LiquidAlphaConsensusMode::<Test>::get(netuid), + ConsensusMode::Previous + ); + + // Also verify with liquid alpha enabled + SubtensorModule::set_liquid_alpha_enabled(netuid, true); + + // Should still succeed + assert_ok!(SubtensorModule::set_liquid_alpha_consensus_mode( + signer.clone(), + netuid, + ConsensusMode::Current + )); + + // Verify the mode was updated + assert_eq!( + LiquidAlphaConsensusMode::<Test>::get(netuid), + ConsensusMode::Current + ); + }); +} + +/// 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 (netuid, _hotkey, _coldkey, owner_signer) = setup_network_with_owner(); + let non_owner = U256::from(999); + let non_owner_signer = RuntimeOrigin::signed(non_owner); + + // Non-owner should fail + assert_err!( + SubtensorModule::set_liquid_alpha_consensus_mode( + non_owner_signer, + netuid, + ConsensusMode::Previous + ), + DispatchError::BadOrigin + ); + + // Owner should succeed + assert_ok!(SubtensorModule::set_liquid_alpha_consensus_mode( + owner_signer, + netuid, + ConsensusMode::Previous + )); + }); +} + +/// 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(); + + // Test Current mode + assert_ok!(SubtensorModule::set_liquid_alpha_consensus_mode( + signer.clone(), + netuid, + ConsensusMode::Current + )); + assert_eq!( + LiquidAlphaConsensusMode::<Test>::get(netuid), + ConsensusMode::Current + ); + + // Test Previous mode + assert_ok!(SubtensorModule::set_liquid_alpha_consensus_mode( + signer.clone(), + netuid, + ConsensusMode::Previous + )); + assert_eq!( + LiquidAlphaConsensusMode::<Test>::get(netuid), + ConsensusMode::Previous + ); + + // Test Auto mode + assert_ok!(SubtensorModule::set_liquid_alpha_consensus_mode( + signer.clone(), + netuid, + ConsensusMode::Auto + )); + assert_eq!( + LiquidAlphaConsensusMode::<Test>::get(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; + + // Setup network and test data + setup_consensus_test_environment(netuid); + LiquidAlphaConsensusMode::<Test>::insert(netuid, ConsensusMode::Current); + 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 (res, curr) in result.iter().zip(current_consensus.iter()) { + assert_eq!(res, curr); + } + }); +} + +/// 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; + + // Setup network and test data + setup_consensus_test_environment(netuid); + LiquidAlphaConsensusMode::<Test>::insert(netuid, ConsensusMode::Previous); + let (current_consensus, previous_consensus) = 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 (res, prev) in result.iter().zip(previous_consensus.iter()) { + assert_abs_diff_eq!(res.to_num::<f32>(), prev.to_num::<f32>(), epsilon = 0.001); + } + }); +} + +/// 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; + + // Setup network and test data + setup_consensus_test_environment(netuid); + LiquidAlphaConsensusMode::<Test>::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 + 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" + ); + } + + // 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 (res, prev) in result.iter().zip(previous_consensus.iter()) { + assert_abs_diff_eq!(res.to_num::<f32>(), prev.to_num::<f32>(), epsilon = 0.001); + } + }); +} diff --git a/pallets/subtensor/src/tests/epoch.rs b/pallets/subtensor/src/tests/epoch.rs index 3775efbea0..d0b47c4c54 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) + LiquidAlphaConsensusMode::<Test>::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::<Test>(); + 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<I32F32>, Vec<I32F32>) { + 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<u16> = previous_consensus + .iter() + .map(|value| { + value + .saturating_mul(I32F32::saturating_from_num(u16::MAX)) + .saturating_to_num::<u16>() + }) + .collect(); + Consensus::<Test>::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/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;