From 3ad54bb059af642008c3a827cce6961233335adc Mon Sep 17 00:00:00 2001 From: Aliaksandr Tsurko Date: Thu, 2 Jan 2025 19:50:53 +0100 Subject: [PATCH 01/20] Extend staking contract with proxy setting --- runtime/src/precompiles/solidity/staking.abi | 28 ++++++++- runtime/src/precompiles/solidity/staking.sol | 14 +++++ runtime/src/precompiles/staking.rs | 60 ++++++++++++++------ 3 files changed, 85 insertions(+), 17 deletions(-) diff --git a/runtime/src/precompiles/solidity/staking.abi b/runtime/src/precompiles/solidity/staking.abi index 44b1829c4..39ff90d57 100644 --- a/runtime/src/precompiles/solidity/staking.abi +++ b/runtime/src/precompiles/solidity/staking.abi @@ -1,4 +1,17 @@ [ + { + "inputs": [ + { + "internalType": "bytes32", + "name": "delegate", + "type": "bytes32" + } + ], + "name": "addProxy", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, { "inputs": [ { @@ -17,6 +30,19 @@ "stateMutability": "payable", "type": "function" }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "delegate", + "type": "bytes32" + } + ], + "name": "removeProxy", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, { "inputs": [ { @@ -37,7 +63,7 @@ ], "name": "removeStake", "outputs": [], - "stateMutability": "payable", + "stateMutability": "nonpayable", "type": "function" } ] diff --git a/runtime/src/precompiles/solidity/staking.sol b/runtime/src/precompiles/solidity/staking.sol index ec7fb7297..0710b23e5 100644 --- a/runtime/src/precompiles/solidity/staking.sol +++ b/runtime/src/precompiles/solidity/staking.sol @@ -42,4 +42,18 @@ interface IStaking { * - The existing stake amount must be not lower than specified amount */ function removeStake(bytes32 hotkey, uint256 amount, uint16 netuid) external; + + /** + * @dev Delegates staking to a proxy account. + * + * @param delegate The public key (32 bytes) of the delegate. + */ + function addProxy(bytes32 delegate) external; + + /** + * @dev Removes staking proxy account. + * + * @param delegate The public key (32 bytes) of the delegate. + */ + function removeProxy(bytes32 delegate) external; } diff --git a/runtime/src/precompiles/staking.rs b/runtime/src/precompiles/staking.rs index e6237dfcf..53c0904b8 100644 --- a/runtime/src/precompiles/staking.rs +++ b/runtime/src/precompiles/staking.rs @@ -36,7 +36,10 @@ use sp_runtime::traits::Dispatchable; use sp_runtime::traits::{BlakeTwo256, UniqueSaturatedInto}; use sp_runtime::AccountId32; -use crate::precompiles::{get_method_id, get_slice}; +use crate::{ + precompiles::{get_method_id, get_slice}, + ProxyType, +}; use sp_std::vec; use crate::{Runtime, RuntimeCall}; @@ -52,21 +55,23 @@ impl StakingPrecompile { .get(4..) .map_or_else(vec::Vec::new, |slice| slice.to_vec()); // Avoiding borrowing conflicts - match method_id { - id if id == get_method_id("addStake(bytes32,uint16)") => { - Self::add_stake(handle, &method_input) - } - id if id == get_method_id("removeStake(bytes32,uint256,uint16)") => { - Self::remove_stake(handle, &method_input) - } - _ => Err(PrecompileFailure::Error { + if method_id == get_method_id("addStake(bytes32,uint16)") { + Self::add_stake(handle, &method_input) + } else if method_id == get_method_id("removeStake(bytes32,uint256,uint16)") { + Self::remove_stake(handle, &method_input) + } else if method_id == get_method_id("addProxy(bytes32)") { + Self::add_proxy(handle, &method_input) + } else if method_id == get_method_id("removeProxy(bytes32)") { + Self::remove_proxy(handle, &method_input) + } else { + Err(PrecompileFailure::Error { exit_status: ExitError::InvalidRange, - }), + }) } } fn add_stake(handle: &mut impl PrecompileHandle, data: &[u8]) -> PrecompileResult { - let hotkey = Self::parse_hotkey(data)?.into(); + let hotkey = Self::parse_pub_key(data)?.into(); let amount: U256 = handle.context().apparent_value; let amount_sub = ::BalanceConverter::into_substrate_balance(amount) @@ -80,8 +85,9 @@ impl StakingPrecompile { // Dispatch the add_stake call Self::dispatch(handle, call) } + fn remove_stake(handle: &mut impl PrecompileHandle, data: &[u8]) -> PrecompileResult { - let hotkey = Self::parse_hotkey(data)?.into(); + let hotkey = Self::parse_pub_key(data)?.into(); // We have to treat this as uint256 (because of Solidity ABI encoding rules, it pads uint64), // but this will never exceed 8 bytes, se we will ignore higher bytes and will only use lower @@ -101,15 +107,37 @@ impl StakingPrecompile { Self::dispatch(handle, call) } - fn parse_hotkey(data: &[u8]) -> Result<[u8; 32], PrecompileFailure> { + fn add_proxy(handle: &mut impl PrecompileHandle, data: &[u8]) -> PrecompileResult { + let delegate = Self::parse_pub_key(data)?; + let call = RuntimeCall::Proxy(pallet_proxy::Call::::add_proxy { + delegate, + proxy_type: ProxyType::Staking, + delay: 0, + }); + + Self::dispatch(handle, call) + } + + fn remove_proxy(handle: &mut impl PrecompileHandle, data: &[u8]) -> PrecompileResult { + let delegate = Self::parse_pub_key(data)?; + let call = RuntimeCall::Proxy(pallet_proxy::Call::::remove_proxy { + delegate, + proxy_type: ProxyType::Staking, + delay: 0, + }); + + Self::dispatch(handle, call) + } + + fn parse_pub_key(data: &[u8]) -> Result<[u8; 32], PrecompileFailure> { if data.len() < 32 { return Err(PrecompileFailure::Error { exit_status: ExitError::InvalidRange, }); } - let mut hotkey = [0u8; 32]; - hotkey.copy_from_slice(get_slice(data, 0, 32)?); - Ok(hotkey) + let mut pubkey = [0u8; 32]; + pubkey.copy_from_slice(get_slice(data, 0, 32)?); + Ok(pubkey) } fn dispatch(handle: &mut impl PrecompileHandle, call: RuntimeCall) -> PrecompileResult { From d581823d3a7f8de0b2b7453ed95637a6e25b5b79 Mon Sep 17 00:00:00 2001 From: Aliaksandr Tsurko Date: Thu, 2 Jan 2025 20:47:22 +0100 Subject: [PATCH 02/20] Reformat --- runtime/src/precompiles/staking.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/runtime/src/precompiles/staking.rs b/runtime/src/precompiles/staking.rs index 53c0904b8..60d38ec49 100644 --- a/runtime/src/precompiles/staking.rs +++ b/runtime/src/precompiles/staking.rs @@ -108,7 +108,7 @@ impl StakingPrecompile { } fn add_proxy(handle: &mut impl PrecompileHandle, data: &[u8]) -> PrecompileResult { - let delegate = Self::parse_pub_key(data)?; + let delegate = sp_runtime::MultiAddress::Address32(Self::parse_pub_key(data)?); let call = RuntimeCall::Proxy(pallet_proxy::Call::::add_proxy { delegate, proxy_type: ProxyType::Staking, @@ -119,7 +119,7 @@ impl StakingPrecompile { } fn remove_proxy(handle: &mut impl PrecompileHandle, data: &[u8]) -> PrecompileResult { - let delegate = Self::parse_pub_key(data)?; + let delegate = sp_runtime::MultiAddress::Address32(Self::parse_pub_key(data)?); let call = RuntimeCall::Proxy(pallet_proxy::Call::::remove_proxy { delegate, proxy_type: ProxyType::Staking, From 3c159bf98959c1044f1c451782c624e837fe6f60 Mon Sep 17 00:00:00 2001 From: Aliaksandr Tsurko Date: Tue, 14 Jan 2025 03:15:20 +0100 Subject: [PATCH 03/20] Fix account id lookup in proxy precompile --- runtime/src/precompiles/staking.rs | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/runtime/src/precompiles/staking.rs b/runtime/src/precompiles/staking.rs index 60d38ec49..51dd350af 100644 --- a/runtime/src/precompiles/staking.rs +++ b/runtime/src/precompiles/staking.rs @@ -31,9 +31,9 @@ use pallet_evm::{ ExitError, ExitSucceed, PrecompileFailure, PrecompileHandle, PrecompileOutput, PrecompileResult, }; use sp_core::crypto::Ss58Codec; -use sp_core::U256; +use sp_core::{U256, H256}; use sp_runtime::traits::Dispatchable; -use sp_runtime::traits::{BlakeTwo256, UniqueSaturatedInto}; +use sp_runtime::traits::{BlakeTwo256, UniqueSaturatedInto, StaticLookup}; use sp_runtime::AccountId32; use crate::{ @@ -108,7 +108,8 @@ impl StakingPrecompile { } fn add_proxy(handle: &mut impl PrecompileHandle, data: &[u8]) -> PrecompileResult { - let delegate = sp_runtime::MultiAddress::Address32(Self::parse_pub_key(data)?); + let delegate = AccountId32::from(Self::parse_pub_key(data)?); + let delegate = ::Lookup::unlookup(delegate); let call = RuntimeCall::Proxy(pallet_proxy::Call::::add_proxy { delegate, proxy_type: ProxyType::Staking, @@ -119,7 +120,8 @@ impl StakingPrecompile { } fn remove_proxy(handle: &mut impl PrecompileHandle, data: &[u8]) -> PrecompileResult { - let delegate = sp_runtime::MultiAddress::Address32(Self::parse_pub_key(data)?); + let delegate = AccountId32::from(Self::parse_pub_key(data)?); + let delegate = ::Lookup::unlookup(delegate); let call = RuntimeCall::Proxy(pallet_proxy::Call::::remove_proxy { delegate, proxy_type: ProxyType::Staking, From 2874d95411bd04cb0b46733cc6e578e9a152900f Mon Sep 17 00:00:00 2001 From: Aliaksandr Tsurko Date: Tue, 14 Jan 2025 23:53:48 +0100 Subject: [PATCH 04/20] Lint --- runtime/src/precompiles/staking.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/runtime/src/precompiles/staking.rs b/runtime/src/precompiles/staking.rs index a8f54498f..86d257a19 100644 --- a/runtime/src/precompiles/staking.rs +++ b/runtime/src/precompiles/staking.rs @@ -31,7 +31,7 @@ use pallet_evm::{ ExitError, ExitSucceed, PrecompileFailure, PrecompileHandle, PrecompileOutput, PrecompileResult, }; use sp_core::crypto::Ss58Codec; -use sp_core::{H256, U256}; +use sp_core::U256; use sp_runtime::traits::Dispatchable; use sp_runtime::traits::{BlakeTwo256, StaticLookup, UniqueSaturatedInto}; use sp_runtime::AccountId32; From 752abc293c2c5a65a12e7ad7a8db3b335ec0d8e3 Mon Sep 17 00:00:00 2001 From: Greg Zaitsev Date: Wed, 15 Jan 2025 16:39:42 -0500 Subject: [PATCH 05/20] Re-add move_stake extrinsic and tests --- pallets/subtensor/src/macros/dispatches.rs | 41 + pallets/subtensor/src/macros/events.rs | 2 + pallets/subtensor/src/staking/mod.rs | 1 + pallets/subtensor/src/staking/move_stake.rs | 7 +- pallets/subtensor/src/tests/mod.rs | 1 + pallets/subtensor/src/tests/move_stake.rs | 783 ++++++++++++++++++++ 6 files changed, 834 insertions(+), 1 deletion(-) create mode 100644 pallets/subtensor/src/tests/move_stake.rs diff --git a/pallets/subtensor/src/macros/dispatches.rs b/pallets/subtensor/src/macros/dispatches.rs index c101cff5c..7073a1413 100644 --- a/pallets/subtensor/src/macros/dispatches.rs +++ b/pallets/subtensor/src/macros/dispatches.rs @@ -1555,5 +1555,46 @@ mod dispatches { pub fn unstake_all_alpha(origin: OriginFor, hotkey: T::AccountId) -> DispatchResult { Self::do_unstake_all_alpha(origin, hotkey) } + + /// ---- The implementation for the extrinsic move_stake: Moves specified amount of stake from a hotkey to another across subnets. + /// + /// # Args: + /// * `origin` - (::Origin): + /// - The signature of the caller's coldkey. + /// + /// * `origin_hotkey` (T::AccountId): + /// - The hotkey account to move stake from. + /// + /// * `destination_hotkey` (T::AccountId): + /// - The hotkey account to move stake to. + /// + /// * `origin_netuid` (T::AccountId): + /// - The subnet ID to move stake from. + /// + /// * `destination_netuid` (T::AccountId): + /// - The subnet ID to move stake to. + /// + /// * `alpha_amount` (T::AccountId): + /// - The alpha stake amount to move. + /// + #[pallet::call_index(85)] + #[pallet::weight((Weight::from_parts(3_000_000, 0).saturating_add(T::DbWeight::get().writes(1)), DispatchClass::Operational, Pays::No))] + pub fn move_stake( + origin: T::RuntimeOrigin, + origin_hotkey: T::AccountId, + destination_hotkey: T::AccountId, + origin_netuid: u16, + destination_netuid: u16, + alpha_amount: u64, + ) -> DispatchResult { + Self::do_move_stake( + origin, + origin_hotkey, + destination_hotkey, + origin_netuid, + destination_netuid, + alpha_amount, + ) + } } } diff --git a/pallets/subtensor/src/macros/events.rs b/pallets/subtensor/src/macros/events.rs index 7ef00b1e3..ff61784e7 100644 --- a/pallets/subtensor/src/macros/events.rs +++ b/pallets/subtensor/src/macros/events.rs @@ -17,6 +17,8 @@ mod events { StakeAdded(T::AccountId, T::AccountId, u64, u64, u16), /// stake has been removed from the hotkey staking account onto the coldkey account. StakeRemoved(T::AccountId, T::AccountId, u64, u64, u16), + /// stake has been moved from origin (hotkey, subnet ID) to destination (hotkey, subnet ID). + StakeMoved(T::AccountId, T::AccountId, u16, T::AccountId, u16, u64), /// a caller successfully sets their weights on a subnetwork. WeightsSet(u16, u16), /// a new neuron account has been registered to the chain. diff --git a/pallets/subtensor/src/staking/mod.rs b/pallets/subtensor/src/staking/mod.rs index 7508b9d2a..2b222036c 100644 --- a/pallets/subtensor/src/staking/mod.rs +++ b/pallets/subtensor/src/staking/mod.rs @@ -3,6 +3,7 @@ pub mod add_stake; pub mod decrease_take; pub mod helpers; pub mod increase_take; +pub mod move_stake; pub mod remove_stake; pub mod set_children; pub mod stake_utils; diff --git a/pallets/subtensor/src/staking/move_stake.rs b/pallets/subtensor/src/staking/move_stake.rs index a5794fe67..81cfef289 100644 --- a/pallets/subtensor/src/staking/move_stake.rs +++ b/pallets/subtensor/src/staking/move_stake.rs @@ -57,7 +57,11 @@ impl Pallet { ); // --- 6. Get the current alpha stake for the origin hotkey-coldkey pair in the origin subnet - let origin_alpha = Self::get_stake_for_hotkey_and_coldkey_on_subnet( &origin_hotkey, &coldkey, origin_netuid ); + let origin_alpha = Self::get_stake_for_hotkey_and_coldkey_on_subnet( + &origin_hotkey, + &coldkey, + origin_netuid, + ); ensure!( alpha_amount <= origin_alpha, Error::::NotEnoughStakeToWithdraw @@ -94,6 +98,7 @@ impl Pallet { origin_netuid, destination_hotkey, destination_netuid, + origin_tao, )); // -- 10. Ok and return. diff --git a/pallets/subtensor/src/tests/mod.rs b/pallets/subtensor/src/tests/mod.rs index e1b01ab81..e0fef9d55 100644 --- a/pallets/subtensor/src/tests/mod.rs +++ b/pallets/subtensor/src/tests/mod.rs @@ -7,6 +7,7 @@ mod epoch; mod math; mod migration; mod mock; +mod move_stake; mod networks; mod neuron_info; mod registration; diff --git a/pallets/subtensor/src/tests/move_stake.rs b/pallets/subtensor/src/tests/move_stake.rs new file mode 100644 index 000000000..b40ea79c4 --- /dev/null +++ b/pallets/subtensor/src/tests/move_stake.rs @@ -0,0 +1,783 @@ +use super::mock::*; +use crate::*; +use approx::assert_abs_diff_eq; +use frame_support::{assert_err, assert_noop, assert_ok}; +use sp_core::U256; + +// 1. test_do_move_success +// Description: Test a successful move of stake between two hotkeys in the same subnet +// SKIP_WASM_BUILD=1 RUST_LOG=debug cargo test --test move -- test_do_move_success --exact --nocapture +#[test] +fn test_do_move_success() { + new_test_ext(1).execute_with(|| { + let subnet_owner_coldkey = U256::from(1001); + let subnet_owner_hotkey = U256::from(1002); + let netuid: u16 = add_dynamic_network(&subnet_owner_hotkey, &subnet_owner_coldkey); + let coldkey = U256::from(1); + let origin_hotkey = U256::from(2); + let destination_hotkey = U256::from(3); + let stake_amount = 1000; + + // Set up initial stake + SubtensorModule::create_account_if_non_existent(&coldkey, &origin_hotkey); + SubtensorModule::create_account_if_non_existent(&coldkey, &destination_hotkey); + SubtensorModule::stake_into_subnet(&origin_hotkey, &coldkey, netuid, stake_amount); + let alpha = SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( + &origin_hotkey, + &coldkey, + netuid, + ); + + // Perform the move + assert_ok!(SubtensorModule::do_move_stake( + RuntimeOrigin::signed(coldkey), + origin_hotkey, + destination_hotkey, + netuid, + netuid, + alpha, + )); + + // Check that the stake has been moved + assert_eq!( + SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( + &origin_hotkey, + &coldkey, + netuid + ), + 0 + ); + assert_abs_diff_eq!( + SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( + &destination_hotkey, + &coldkey, + netuid + ), + stake_amount, + epsilon = 5 + ); + }); +} + +// 2. test_do_move_different_subnets +// Description: Test moving stake between two hotkeys in different subnets +// SKIP_WASM_BUILD=1 RUST_LOG=debug cargo test --test move -- test_do_move_different_subnets --exact --nocapture +#[test] +fn test_do_move_different_subnets() { + new_test_ext(1).execute_with(|| { + let subnet_owner_coldkey = U256::from(1001); + let subnet_owner_hotkey = U256::from(1002); + let origin_netuid = add_dynamic_network(&subnet_owner_hotkey, &subnet_owner_coldkey); + let destination_netuid = add_dynamic_network(&subnet_owner_hotkey, &subnet_owner_coldkey); + let coldkey = U256::from(1); + let origin_hotkey = U256::from(2); + let destination_hotkey = U256::from(3); + let stake_amount = 1000; + + // Set up initial stake and subnets + SubtensorModule::create_account_if_non_existent(&coldkey, &origin_hotkey); + SubtensorModule::create_account_if_non_existent(&coldkey, &destination_hotkey); + SubtensorModule::stake_into_subnet(&origin_hotkey, &coldkey, origin_netuid, stake_amount); + let alpha = SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( + &origin_hotkey, + &coldkey, + origin_netuid, + ); + + // Perform the move + assert_ok!(SubtensorModule::do_move_stake( + RuntimeOrigin::signed(coldkey), + origin_hotkey, + destination_hotkey, + origin_netuid, + destination_netuid, + alpha, + )); + + // Check that the stake has been moved + assert_eq!( + SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( + &origin_hotkey, + &coldkey, + origin_netuid + ), + 0 + ); + assert_abs_diff_eq!( + SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( + &destination_hotkey, + &coldkey, + destination_netuid + ), + stake_amount, + epsilon = 5 + ); + }); +} + +// 4. test_do_move_nonexistent_subnet +// Description: Attempt to move stake to a non-existent subnet, which should fail +// SKIP_WASM_BUILD=1 RUST_LOG=debug cargo test --test move -- test_do_move_nonexistent_subnet --exact --nocapture +#[test] +fn test_do_move_nonexistent_subnet() { + new_test_ext(1).execute_with(|| { + let subnet_owner_coldkey = U256::from(1001); + let subnet_owner_hotkey = U256::from(1002); + let origin_netuid = add_dynamic_network(&subnet_owner_hotkey, &subnet_owner_coldkey); + let coldkey = U256::from(1); + let origin_hotkey = U256::from(2); + let destination_hotkey = U256::from(3); + let nonexistent_netuid = 99; // Assuming this subnet doesn't exist + let stake_amount = 1000; + + // Set up initial stake + SubtensorModule::stake_into_subnet(&origin_hotkey, &coldkey, origin_netuid, stake_amount); + let alpha = SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( + &origin_hotkey, + &coldkey, + origin_netuid, + ); + + // Attempt to move stake to a non-existent subnet + assert_noop!( + SubtensorModule::do_move_stake( + RuntimeOrigin::signed(coldkey), + origin_hotkey, + destination_hotkey, + origin_netuid, + nonexistent_netuid, + alpha, + ), + Error::::SubnetNotExists + ); + + // Check that the stake remains unchanged + assert_abs_diff_eq!( + SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( + &origin_hotkey, + &coldkey, + origin_netuid + ), + stake_amount, + epsilon = 5 + ); + }); +} + +// 5. test_do_move_nonexistent_origin_hotkey +// Description: Attempt to move stake from a non-existent origin hotkey, which should fail +// SKIP_WASM_BUILD=1 RUST_LOG=debug cargo test --test move -- test_do_move_nonexistent_origin_hotkey --exact --nocapture +#[test] +fn test_do_move_nonexistent_origin_hotkey() { + new_test_ext(1).execute_with(|| { + let subnet_owner_coldkey = U256::from(1001); + let subnet_owner_hotkey = U256::from(1002); + let netuid = add_dynamic_network(&subnet_owner_hotkey, &subnet_owner_coldkey); + let coldkey = U256::from(1); + let nonexistent_origin_hotkey = U256::from(99); // Assuming this hotkey doesn't exist + let destination_hotkey = U256::from(3); + + // Attempt to move stake from a non-existent origin hotkey + assert_noop!( + SubtensorModule::do_move_stake( + RuntimeOrigin::signed(coldkey), + nonexistent_origin_hotkey, + destination_hotkey, + netuid, + netuid, + 123 + ), + Error::::HotKeyAccountNotExists + ); + + // Check that no stake was moved + assert_eq!( + SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( + &nonexistent_origin_hotkey, + &coldkey, + netuid + ), + 0 + ); + assert_eq!( + SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( + &destination_hotkey, + &coldkey, + netuid + ), + 0 + ); + }); +} + +// 6. test_do_move_nonexistent_destination_hotkey +// Description: Attempt to move stake to a non-existent destination hotkey, which should fail +// SKIP_WASM_BUILD=1 RUST_LOG=debug cargo test --test move -- test_do_move_nonexistent_destination_hotkey --exact --nocapture +#[test] +fn test_do_move_nonexistent_destination_hotkey() { + new_test_ext(1).execute_with(|| { + let coldkey = U256::from(1); + let origin_hotkey = U256::from(2); + let nonexistent_destination_hotkey = U256::from(99); // Assuming this hotkey doesn't exist + let netuid = 1; + let stake_amount = 1000; + + // Set up initial stake + SubtensorModule::stake_into_subnet(&origin_hotkey, &coldkey, netuid, stake_amount); + + // Attempt to move stake from a non-existent origin hotkey + add_network(netuid, 0, 0); + assert_noop!( + SubtensorModule::do_move_stake( + RuntimeOrigin::signed(coldkey), + origin_hotkey, + nonexistent_destination_hotkey, + netuid, + netuid, + 123 + ), + Error::::HotKeyAccountNotExists + ); + + // Check that the stake was moved successfully + assert_eq!( + SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( + &origin_hotkey, + &coldkey, + netuid + ), + 1000 + ); + assert_eq!( + SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( + &nonexistent_destination_hotkey, + &coldkey, + netuid + ), + 0 + ); + }); +} + +// 7. test_do_move_zero_stake +// Description: Test moving zero stake, which should succeed but have no effect +// SKIP_WASM_BUILD=1 RUST_LOG=debug cargo test --test move -- test_do_move_zero_stake --exact --nocapture +#[test] +fn test_do_move_zero_stake() { + new_test_ext(1).execute_with(|| { + let subnet_owner_coldkey = U256::from(1001); + let subnet_owner_hotkey = U256::from(1002); + let netuid = add_dynamic_network(&subnet_owner_hotkey, &subnet_owner_coldkey); + let coldkey = U256::from(1); + let origin_hotkey = U256::from(2); + let destination_hotkey = U256::from(3); + + // Attempt to move zero stake + SubtensorModule::create_account_if_non_existent(&coldkey, &origin_hotkey); + SubtensorModule::create_account_if_non_existent(&coldkey, &destination_hotkey); + assert_ok!(SubtensorModule::do_move_stake( + RuntimeOrigin::signed(coldkey), + origin_hotkey, + destination_hotkey, + netuid, + netuid, + 0, + )); + + // Check that no stake was moved + assert_eq!( + SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( + &origin_hotkey, + &coldkey, + netuid + ), + 0 + ); + assert_eq!( + SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( + &destination_hotkey, + &coldkey, + netuid + ), + 0 + ); + }); +} + +// 8. test_do_move_all_stake +// Description: Test moving all stake from one hotkey to another +// SKIP_WASM_BUILD=1 RUST_LOG=debug cargo test --test move -- test_do_move_all_stake --exact --nocapture +#[test] +fn test_do_move_all_stake() { + new_test_ext(1).execute_with(|| { + let subnet_owner_coldkey = U256::from(1001); + let subnet_owner_hotkey = U256::from(1002); + let netuid = add_dynamic_network(&subnet_owner_hotkey, &subnet_owner_coldkey); + let coldkey = U256::from(1); + let origin_hotkey = U256::from(2); + let destination_hotkey = U256::from(3); + let stake_amount = 1000; + + // Set up initial stake + SubtensorModule::stake_into_subnet(&origin_hotkey, &coldkey, netuid, stake_amount); + let alpha = SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( + &origin_hotkey, + &coldkey, + netuid, + ); + + // Move all stake + SubtensorModule::create_account_if_non_existent(&coldkey, &origin_hotkey); + SubtensorModule::create_account_if_non_existent(&coldkey, &destination_hotkey); + assert_ok!(SubtensorModule::do_move_stake( + RuntimeOrigin::signed(coldkey), + origin_hotkey, + destination_hotkey, + netuid, + netuid, + alpha, + )); + + // Check that all stake was moved + assert_eq!( + SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( + &origin_hotkey, + &coldkey, + netuid + ), + 0 + ); + assert_abs_diff_eq!( + SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( + &destination_hotkey, + &coldkey, + netuid + ), + stake_amount, + epsilon = 5 + ); + }); +} + +#[test] +fn test_do_move_half_stake() { + new_test_ext(1).execute_with(|| { + let subnet_owner_coldkey = U256::from(1001); + let subnet_owner_hotkey = U256::from(1002); + let netuid = add_dynamic_network(&subnet_owner_hotkey, &subnet_owner_coldkey); + let coldkey = U256::from(1); + let origin_hotkey = U256::from(2); + let destination_hotkey = U256::from(3); + let stake_amount = 1000; + + // Set up initial stake + SubtensorModule::stake_into_subnet(&origin_hotkey, &coldkey, netuid, stake_amount); + let alpha = SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( + &origin_hotkey, + &coldkey, + netuid, + ); + + // Move all stake + SubtensorModule::create_account_if_non_existent(&coldkey, &origin_hotkey); + SubtensorModule::create_account_if_non_existent(&coldkey, &destination_hotkey); + assert_ok!(SubtensorModule::do_move_stake( + RuntimeOrigin::signed(coldkey), + origin_hotkey, + destination_hotkey, + netuid, + netuid, + alpha / 2, + )); + + // Check that all stake was moved + assert_abs_diff_eq!( + SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( + &origin_hotkey, + &coldkey, + netuid + ), + stake_amount / 2, + epsilon = 5 + ); + assert_abs_diff_eq!( + SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( + &destination_hotkey, + &coldkey, + netuid + ), + stake_amount / 2, + epsilon = 5 + ); + }); +} + +// 9. test_do_move_partial_stake +// Description: Test moving a portion of stake from one hotkey to another +// SKIP_WASM_BUILD=1 RUST_LOG=debug cargo test --test move -- test_do_move_partial_stake --exact --nocapture +#[test] +fn test_do_move_partial_stake() { + new_test_ext(1).execute_with(|| { + let subnet_owner_coldkey = U256::from(1001); + let subnet_owner_hotkey = U256::from(1002); + let netuid = add_dynamic_network(&subnet_owner_hotkey, &subnet_owner_coldkey); + let coldkey = U256::from(1); + let origin_hotkey = U256::from(2); + let destination_hotkey = U256::from(3); + let total_stake = 1000; + + // Set up initial stake + SubtensorModule::stake_into_subnet(&origin_hotkey, &coldkey, netuid, total_stake); + let alpha = SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( + &origin_hotkey, + &coldkey, + netuid, + ); + + // Move partial stake + SubtensorModule::create_account_if_non_existent(&coldkey, &origin_hotkey); + SubtensorModule::create_account_if_non_existent(&coldkey, &destination_hotkey); + assert_ok!(SubtensorModule::do_move_stake( + RuntimeOrigin::signed(coldkey), + origin_hotkey, + destination_hotkey, + netuid, + netuid, + alpha, + )); + + // Check that the correct amount of stake was moved + assert_eq!( + SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( + &origin_hotkey, + &coldkey, + netuid + ), + 0 + ); + assert_abs_diff_eq!( + SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( + &destination_hotkey, + &coldkey, + netuid + ), + total_stake, + epsilon = 5 + ); + }); +} + +// 10. test_do_move_multiple_times +// Description: Test moving stake multiple times between the same hotkeys +// SKIP_WASM_BUILD=1 RUST_LOG=debug cargo test --test move -- test_do_move_multiple_times --exact --nocapture +#[test] +fn test_do_move_multiple_times() { + new_test_ext(1).execute_with(|| { + let subnet_owner_coldkey = U256::from(1001); + let subnet_owner_hotkey = U256::from(1002); + let netuid = add_dynamic_network(&subnet_owner_hotkey, &subnet_owner_coldkey); + let coldkey = U256::from(1); + let hotkey1 = U256::from(2); + let hotkey2 = U256::from(3); + let initial_stake = 1000; + + // Set up initial stake + SubtensorModule::create_account_if_non_existent(&coldkey, &hotkey1); + SubtensorModule::create_account_if_non_existent(&coldkey, &hotkey2); + SubtensorModule::stake_into_subnet(&hotkey1, &coldkey, netuid, initial_stake); + + // Move stake multiple times + for _ in 0..3 { + let alpha1 = SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( + &hotkey1, &coldkey, netuid, + ); + assert_ok!(SubtensorModule::do_move_stake( + RuntimeOrigin::signed(coldkey), + hotkey1, + hotkey2, + netuid, + netuid, + alpha1, + )); + let alpha2 = SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( + &hotkey2, &coldkey, netuid, + ); + assert_ok!(SubtensorModule::do_move_stake( + RuntimeOrigin::signed(coldkey), + hotkey2, + hotkey1, + netuid, + netuid, + alpha2, + )); + } + + // Check final stake distribution + assert_abs_diff_eq!( + SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet(&hotkey1, &coldkey, netuid), + initial_stake, + epsilon = 10 + ); + assert_eq!( + SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet(&hotkey2, &coldkey, netuid), + 0 + ); + }); +} + +// 13. test_do_move_wrong_origin +// Description: Attempt to move stake with a different origin than the coldkey, which should fail +// SKIP_WASM_BUILD=1 RUST_LOG=debug cargo test --test move -- test_do_move_wrong_origin --exact --nocapture +#[test] +fn test_do_move_wrong_origin() { + new_test_ext(1).execute_with(|| { + let coldkey = U256::from(1); + let wrong_coldkey = U256::from(99); + let origin_hotkey = U256::from(2); + let destination_hotkey = U256::from(3); + let netuid = 1; + let stake_amount = 1000; + + // Set up initial stake + SubtensorModule::stake_into_subnet(&origin_hotkey, &coldkey, netuid, stake_amount); + let alpha = SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( + &origin_hotkey, + &coldkey, + netuid, + ); + + // Attempt to move stake with wrong origin + add_network(netuid, 0, 0); + SubtensorModule::create_account_if_non_existent(&coldkey, &origin_hotkey); + SubtensorModule::create_account_if_non_existent(&coldkey, &destination_hotkey); + assert_err!( + SubtensorModule::do_move_stake( + RuntimeOrigin::signed(wrong_coldkey), + origin_hotkey, + destination_hotkey, + netuid, + netuid, + alpha, + ), + Error::::NotEnoughStakeToWithdraw + ); + + // Check that no stake was moved + assert_eq!( + SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( + &origin_hotkey, + &coldkey, + netuid + ), + stake_amount + ); + assert_eq!( + SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( + &destination_hotkey, + &coldkey, + netuid + ), + 0 + ); + }); +} + +// 14. test_do_move_same_hotkey +// Description: Attempt to move stake to the same hotkey, which should fail or have no effect +// SKIP_WASM_BUILD=1 RUST_LOG=debug cargo test --test move -- test_do_move_same_hotkey --exact --nocapture +#[test] +fn test_do_move_same_hotkey() { + new_test_ext(1).execute_with(|| { + let subnet_owner_coldkey = U256::from(1001); + let subnet_owner_hotkey = U256::from(1002); + let netuid = add_dynamic_network(&subnet_owner_hotkey, &subnet_owner_coldkey); + let coldkey = U256::from(1); + let hotkey = U256::from(2); + let stake_amount = 1000; + + // Set up initial stake + SubtensorModule::create_account_if_non_existent(&coldkey, &hotkey); + SubtensorModule::stake_into_subnet(&hotkey, &coldkey, netuid, stake_amount); + let alpha = + SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet(&hotkey, &coldkey, netuid); + + // Attempt to move stake to the same hotkey + assert_ok!(SubtensorModule::do_move_stake( + RuntimeOrigin::signed(coldkey), + hotkey, + hotkey, + netuid, + netuid, + alpha, + )); + + // Check that stake remains unchanged + assert_abs_diff_eq!( + SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet(&hotkey, &coldkey, netuid), + alpha, + epsilon = 5 + ); + }); +} + +// 15. test_do_move_event_emission +// Description: Verify that the correct event is emitted after a successful move +// SKIP_WASM_BUILD=1 RUST_LOG=debug cargo test --test move -- test_do_move_event_emission --exact --nocapture +#[test] +fn test_do_move_event_emission() { + new_test_ext(1).execute_with(|| { + let subnet_owner_coldkey = U256::from(1001); + let subnet_owner_hotkey = U256::from(1002); + let netuid = add_dynamic_network(&subnet_owner_hotkey, &subnet_owner_coldkey); + let coldkey = U256::from(1); + let origin_hotkey = U256::from(2); + let destination_hotkey = U256::from(3); + let stake_amount = 1000; + + // Set up initial stake + SubtensorModule::create_account_if_non_existent(&coldkey, &origin_hotkey); + SubtensorModule::create_account_if_non_existent(&coldkey, &destination_hotkey); + SubtensorModule::stake_into_subnet(&origin_hotkey, &coldkey, netuid, stake_amount); + let alpha = SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( + &origin_hotkey, + &coldkey, + netuid, + ); + + // Move stake and capture events + System::reset_events(); + assert_ok!(SubtensorModule::do_move_stake( + RuntimeOrigin::signed(coldkey), + origin_hotkey, + destination_hotkey, + netuid, + netuid, + alpha, + )); + + // Check for the correct event emission + System::assert_last_event( + Event::StakeMoved( + coldkey, + origin_hotkey, + netuid, + destination_hotkey, + netuid, + alpha, + ) + .into(), + ); + }); +} + +// 16. test_do_move_storage_updates +// Description: Verify that all relevant storage items are correctly updated after a move +// SKIP_WASM_BUILD=1 RUST_LOG=debug cargo test --test move -- test_do_move_storage_updates --exact --nocapture +#[test] +fn test_do_move_storage_updates() { + new_test_ext(1).execute_with(|| { + let subnet_owner_coldkey = U256::from(1001); + let subnet_owner_hotkey = U256::from(1002); + let origin_netuid = add_dynamic_network(&subnet_owner_hotkey, &subnet_owner_coldkey); + let destination_netuid = add_dynamic_network(&subnet_owner_hotkey, &subnet_owner_coldkey); + let coldkey = U256::from(1); + let origin_hotkey = U256::from(2); + let destination_hotkey = U256::from(3); + let stake_amount = 1000; + + // Set up initial stake + SubtensorModule::stake_into_subnet(&origin_hotkey, &coldkey, origin_netuid, stake_amount); + + // Move stake + SubtensorModule::create_account_if_non_existent(&coldkey, &origin_hotkey); + SubtensorModule::create_account_if_non_existent(&coldkey, &destination_hotkey); + let alpha = SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( + &origin_hotkey, + &coldkey, + origin_netuid, + ); + + assert_ok!(SubtensorModule::do_move_stake( + RuntimeOrigin::signed(coldkey), + origin_hotkey, + destination_hotkey, + origin_netuid, + destination_netuid, + alpha, + )); + + // Verify storage updates + assert_eq!( + SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( + &origin_hotkey, + &coldkey, + origin_netuid + ), + 0 + ); + assert_abs_diff_eq!( + SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( + &destination_hotkey, + &coldkey, + destination_netuid + ), + alpha, + epsilon = 5 + ); + }); +} + +// 18. test_do_move_max_values +// Description: Test moving the maximum possible stake values to check for overflows +// SKIP_WASM_BUILD=1 RUST_LOG=debug cargo test --test move -- test_do_move_max_values --exact --nocapture +#[test] +fn test_do_move_max_values() { + new_test_ext(1).execute_with(|| { + let subnet_owner_coldkey = U256::from(1001); + let subnet_owner_hotkey = U256::from(1002); + let coldkey = U256::from(1); + let origin_hotkey = U256::from(2); + let destination_hotkey = U256::from(3); + let max_stake = u64::MAX; + let netuid: u16 = add_dynamic_network(&subnet_owner_hotkey, &subnet_owner_coldkey); + + // Set up initial stake with maximum value + SubtensorModule::create_account_if_non_existent(&coldkey, &origin_hotkey); + SubtensorModule::create_account_if_non_existent(&coldkey, &destination_hotkey); + SubtensorModule::stake_into_subnet(&origin_hotkey, &coldkey, netuid, max_stake); + let alpha = SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( + &origin_hotkey, + &coldkey, + netuid, + ); + + // Move maximum stake + assert_ok!(SubtensorModule::do_move_stake( + RuntimeOrigin::signed(coldkey), + origin_hotkey, + destination_hotkey, + netuid, + netuid, + alpha, + )); + + // Verify stake movement without overflow + assert_eq!( + SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( + &origin_hotkey, + &coldkey, + netuid + ), + 0 + ); + assert_abs_diff_eq!( + SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( + &destination_hotkey, + &coldkey, + netuid + ), + alpha, + epsilon = 5 + ); + }); +} From adb344ca27b6b1639bbc1be862e619e3c9f9337a Mon Sep 17 00:00:00 2001 From: Greg Zaitsev Date: Wed, 15 Jan 2025 17:19:55 -0500 Subject: [PATCH 06/20] Add minimum stake to add_stake --- pallets/subtensor/src/lib.rs | 6 ++ pallets/subtensor/src/macros/errors.rs | 2 + pallets/subtensor/src/staking/add_stake.rs | 7 +++ pallets/subtensor/src/tests/staking.rs | 66 +++++++++++++++------- 4 files changed, 60 insertions(+), 21 deletions(-) diff --git a/pallets/subtensor/src/lib.rs b/pallets/subtensor/src/lib.rs index c895a7e1d..320dd475c 100644 --- a/pallets/subtensor/src/lib.rs +++ b/pallets/subtensor/src/lib.rs @@ -701,6 +701,12 @@ pub mod pallet { 1_000_000_000_000 } + #[pallet::type_value] + /// Default minimum stake. + pub fn DefaultMinStake() -> u64 { + 1_000 + } + #[pallet::type_value] /// Default unicode vector for tau symbol. pub fn DefaultUnicodeVecU8() -> Vec { diff --git a/pallets/subtensor/src/macros/errors.rs b/pallets/subtensor/src/macros/errors.rs index ee9aac2ef..649336674 100644 --- a/pallets/subtensor/src/macros/errors.rs +++ b/pallets/subtensor/src/macros/errors.rs @@ -183,5 +183,7 @@ mod errors { InputLengthsUnequal, /// A transactor exceeded the rate limit for setting weights. CommittingWeightsTooFast, + /// Stake amount is too low. + AmountTooLow, } } diff --git a/pallets/subtensor/src/staking/add_stake.rs b/pallets/subtensor/src/staking/add_stake.rs index 5b3e201cc..e0bee22fe 100644 --- a/pallets/subtensor/src/staking/add_stake.rs +++ b/pallets/subtensor/src/staking/add_stake.rs @@ -1,4 +1,5 @@ use super::*; +use sp_core::Get; impl Pallet { /// ---- The implementation for the extrinsic add_stake: Adds stake to a hotkey account. @@ -61,6 +62,12 @@ impl Pallet { Error::::HotKeyAccountNotExists ); + // Ensure stake_to_ve_added is at least DefaultMinStake + ensure!( + stake_to_be_added >= DefaultMinStake::::get(), + Error::::AmountTooLow + ); + // 5. Ensure the remove operation from the coldkey is a success. let tao_staked: u64 = Self::remove_balance_from_coldkey_account(&coldkey, stake_to_be_added)?; diff --git a/pallets/subtensor/src/tests/staking.rs b/pallets/subtensor/src/tests/staking.rs index a712993e2..ce8738544 100644 --- a/pallets/subtensor/src/tests/staking.rs +++ b/pallets/subtensor/src/tests/staking.rs @@ -1114,60 +1114,82 @@ fn test_clear_small_nominations() { assert_eq!(SubtensorModule::get_owning_coldkey_for_hotkey(&hot2), cold2); // Add stake cold1 --> hot1 (non delegation.) - SubtensorModule::add_balance_to_coldkey_account(&cold1, 5); + SubtensorModule::add_balance_to_coldkey_account(&cold1, 1_000); assert_ok!(SubtensorModule::add_stake( RuntimeOrigin::signed(cold1), hot1, netuid, - 1 + 1_000 + )); + assert_ok!(SubtensorModule::remove_stake( + RuntimeOrigin::signed(cold1), + hot1, + netuid, + SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet(&hot1, &cold1, netuid) - 1 )); assert_eq!( SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet(&hot1, &cold1, netuid), 1 ); - assert_eq!(Balances::free_balance(cold1), 4); // Add stake cold2 --> hot1 (is delegation.) - SubtensorModule::add_balance_to_coldkey_account(&cold2, 5); + SubtensorModule::add_balance_to_coldkey_account(&cold2, 1_000); assert_ok!(SubtensorModule::add_stake( RuntimeOrigin::signed(cold2), hot1, netuid, - 1 + 1_000 + )); + assert_ok!(SubtensorModule::remove_stake( + RuntimeOrigin::signed(cold2), + hot1, + netuid, + SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet(&hot1, &cold2, netuid) - 1 )); assert_eq!( SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet(&hot1, &cold2, netuid), 1 ); - assert_eq!(Balances::free_balance(cold2), 4); // Add stake cold1 --> hot2 (non delegation.) - SubtensorModule::add_balance_to_coldkey_account(&cold1, 5); + SubtensorModule::add_balance_to_coldkey_account(&cold1, 1_000); assert_ok!(SubtensorModule::add_stake( RuntimeOrigin::signed(cold1), hot2, netuid, - 1 + 1_000 + )); + assert_ok!(SubtensorModule::remove_stake( + RuntimeOrigin::signed(cold1), + hot2, + netuid, + SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet(&hot2, &cold1, netuid) - 1 )); assert_eq!( SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet(&hot2, &cold1, netuid), 1 ); - assert_eq!(Balances::free_balance(cold1), 8); + let balance1_before_cleaning = Balances::free_balance(cold1); // Add stake cold2 --> hot2 (is delegation.) - SubtensorModule::add_balance_to_coldkey_account(&cold2, 5); + SubtensorModule::add_balance_to_coldkey_account(&cold2, 1_000); assert_ok!(SubtensorModule::add_stake( RuntimeOrigin::signed(cold2), hot2, netuid, - 1 + 1_000 + )); + assert_ok!(SubtensorModule::remove_stake( + RuntimeOrigin::signed(cold2), + hot2, + netuid, + SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet(&hot2, &cold2, netuid) - 1 )); assert_eq!( SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet(&hot2, &cold2, netuid), 1 ); - assert_eq!(Balances::free_balance(cold2), 8); + let balance2_before_cleaning = Balances::free_balance(cold2); // Run clear all small nominations when min stake is zero (noop) SubtensorModule::set_nominator_min_required_stake(0); @@ -1218,8 +1240,10 @@ fn test_clear_small_nominations() { ); // Balances have been added back into accounts. - assert_eq!(Balances::free_balance(cold1), 9); - assert_eq!(Balances::free_balance(cold2), 9); + let balance1_after_cleaning = Balances::free_balance(cold1); + let balance2_after_cleaning = Balances::free_balance(cold2); + assert_eq!(balance1_before_cleaning + 1, balance1_after_cleaning); + assert_eq!(balance2_before_cleaning + 1, balance2_after_cleaning); assert_eq!( TotalHotkeyAlpha::::get(hot2, netuid), @@ -1644,7 +1668,7 @@ fn test_get_total_delegated_stake_single_delegator() { let delegate_coldkey = U256::from(1); let delegate_hotkey = U256::from(2); let delegator = U256::from(3); - let stake_amount = 999; + let stake_amount = 1_999; let existential_deposit = ExistentialDeposit::get(); let netuid = add_dynamic_network(&subnet_owner_hotkey, &subnet_owner_coldkey); @@ -1753,8 +1777,8 @@ fn test_get_total_delegated_stake_exclude_owner_stake() { let delegate_coldkey = U256::from(1); let delegate_hotkey = U256::from(2); let delegator = U256::from(3); - let owner_stake = 1000; - let delegator_stake = 999; + let owner_stake = 1_000_000; + let delegator_stake = 999_999; let netuid = add_dynamic_network(&delegate_hotkey, &delegate_coldkey); @@ -1792,10 +1816,10 @@ fn test_get_total_delegated_stake_exclude_owner_stake() { let actual_delegated_stake = SubtensorModule::get_total_stake_for_coldkey(&delegate_coldkey); - assert_eq!( - actual_delegated_stake, expected_delegated_stake, - "Delegated stake should exclude owner's stake. Expected: {}, Actual: {}", - expected_delegated_stake, actual_delegated_stake + assert_abs_diff_eq!( + actual_delegated_stake, + expected_delegated_stake, + epsilon = 100 ); }); } From 966a1a9e39e28fbc99b669facc9808ec7572dcbe Mon Sep 17 00:00:00 2001 From: Greg Zaitsev Date: Wed, 15 Jan 2025 17:22:55 -0500 Subject: [PATCH 07/20] Add test for staking too litle --- pallets/subtensor/src/tests/staking.rs | 27 ++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/pallets/subtensor/src/tests/staking.rs b/pallets/subtensor/src/tests/staking.rs index ce8738544..8104fe0fa 100644 --- a/pallets/subtensor/src/tests/staking.rs +++ b/pallets/subtensor/src/tests/staking.rs @@ -1901,3 +1901,30 @@ fn test_mining_emission_distribution_validator_valiminer_miner() { assert_eq!(miner_emission, total_emission / 4); }); } + +// Verify staking too low amount is impossible +#[test] +fn test_staking_too_little_fails() { + new_test_ext(1).execute_with(|| { + let hotkey_account_id = U256::from(533453); + let coldkey_account_id = U256::from(55453); + let amount = 10_000; + + //add network + let netuid: u16 = add_dynamic_network(&hotkey_account_id, &coldkey_account_id); + + // Give it some $$$ in his coldkey balance + SubtensorModule::add_balance_to_coldkey_account(&coldkey_account_id, amount); + + // Coldkey / hotkey 0 decreases take to 5%. This should fail as the minimum take is 9% + assert_err!( + SubtensorModule::add_stake( + RuntimeOrigin::signed(coldkey_account_id), + hotkey_account_id, + netuid, + 1 + ), + Error::::AmountTooLow + ); + }); +} From de5d1cb4e678eb4c6874ac6685cdd74b8ea621cc Mon Sep 17 00:00:00 2001 From: Greg Zaitsev Date: Thu, 16 Jan 2025 11:03:00 -0500 Subject: [PATCH 08/20] Add 1000 rao min stake - tests in progress --- pallets/admin-utils/src/tests/mod.rs | 130 -------------------- pallets/subtensor/src/staking/add_stake.rs | 2 +- pallets/subtensor/src/staking/move_stake.rs | 8 +- pallets/subtensor/src/tests/move_stake.rs | 124 +++++++++---------- pallets/subtensor/src/tests/senate.rs | 14 ++- pallets/subtensor/src/tests/swap_coldkey.rs | 37 ++++-- 6 files changed, 98 insertions(+), 217 deletions(-) diff --git a/pallets/admin-utils/src/tests/mod.rs b/pallets/admin-utils/src/tests/mod.rs index f791e5471..01fb985d9 100644 --- a/pallets/admin-utils/src/tests/mod.rs +++ b/pallets/admin-utils/src/tests/mod.rs @@ -931,136 +931,6 @@ mod sudo_set_nominator_min_required_stake { ); }); } - - #[test] - fn clears_staker_nominations_below_min() { - new_test_ext().execute_with(|| { - System::set_block_number(1); - - // Create accounts. - let netuid = 1; - let hot1 = U256::from(1); - let hot2 = U256::from(2); - let cold1 = U256::from(3); - let cold2 = U256::from(4); - - // SubtensorModule::set_target_stakes_per_interval(10); - // Register network. - add_network(netuid, 0); - - // Register hot1. - register_ok_neuron(netuid, hot1, cold1, 0); - assert_eq!(SubtensorModule::get_owning_coldkey_for_hotkey(&hot1), cold1); - - // Register hot2. - register_ok_neuron(netuid, hot2, cold2, 0); - assert_eq!(SubtensorModule::get_owning_coldkey_for_hotkey(&hot2), cold2); - - // Add stake cold1 --> hot1 (non delegation.) - SubtensorModule::add_balance_to_coldkey_account(&cold1, 5); - assert_ok!(SubtensorModule::add_stake( - <::RuntimeOrigin>::signed(cold1), - hot1, - netuid, - 1 - )); - assert_eq!( - SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet(&hot1, &cold1, netuid), - 1 - ); - assert_eq!(Balances::free_balance(cold1), 4); - - // Add stake cold2 --> hot1 (is delegation.) - SubtensorModule::add_balance_to_coldkey_account(&cold2, 5); - assert_ok!(SubtensorModule::add_stake( - <::RuntimeOrigin>::signed(cold2), - hot1, - netuid, - 1 - )); - assert_eq!( - SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet(&hot1, &cold2, netuid), - 1 - ); - assert_eq!(Balances::free_balance(cold2), 4); - - // Add stake cold1 --> hot2 - SubtensorModule::add_balance_to_coldkey_account(&cold1, 5); - assert_ok!(SubtensorModule::add_stake( - <::RuntimeOrigin>::signed(cold1), - hot2, - netuid, - 1 - )); - assert_eq!( - SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet(&hot2, &cold1, netuid), - 1 - ); - assert_eq!(Balances::free_balance(cold1), 8); - - // Add stake cold2 --> hot2 - SubtensorModule::add_balance_to_coldkey_account(&cold2, 5); - assert_ok!(SubtensorModule::add_stake( - <::RuntimeOrigin>::signed(cold2), - hot2, - netuid, - 1 - )); - assert_eq!( - SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet(&hot2, &cold2, netuid), - 1 - ); - assert_eq!(Balances::free_balance(cold2), 8); - - // Set min stake to 0 (noop) - assert_ok!(AdminUtils::sudo_set_nominator_min_required_stake( - <::RuntimeOrigin>::root(), - 0u64 - )); - assert_eq!( - SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet(&hot1, &cold1, netuid), - 1 - ); - assert_eq!( - SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet(&hot2, &cold1, netuid), - 1 - ); - assert_eq!( - SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet(&hot1, &cold2, netuid), - 1 - ); - assert_eq!( - SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet(&hot2, &cold2, netuid), - 1 - ); - - // Set min nomination to 10: should clear (cold2, hot1) and (cold1, hot2). - assert_ok!(AdminUtils::sudo_set_nominator_min_required_stake( - <::RuntimeOrigin>::root(), - 10u64 - )); - assert_eq!( - SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet(&hot1, &cold1, netuid), - 1 - ); - assert_eq!( - SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet(&hot2, &cold1, netuid), - 0 - ); - assert_eq!( - SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet(&hot1, &cold2, netuid), - 0 - ); - assert_eq!( - SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet(&hot2, &cold2, netuid), - 1 - ); - - // Balances have been added back into accounts. - assert_eq!(Balances::free_balance(cold1), 9); - assert_eq!(Balances::free_balance(cold2), 9); - }); - } } #[test] diff --git a/pallets/subtensor/src/staking/add_stake.rs b/pallets/subtensor/src/staking/add_stake.rs index e0bee22fe..f63735cd3 100644 --- a/pallets/subtensor/src/staking/add_stake.rs +++ b/pallets/subtensor/src/staking/add_stake.rs @@ -62,7 +62,7 @@ impl Pallet { Error::::HotKeyAccountNotExists ); - // Ensure stake_to_ve_added is at least DefaultMinStake + // Ensure stake_to_be_added is at least DefaultMinStake ensure!( stake_to_be_added >= DefaultMinStake::::get(), Error::::AmountTooLow diff --git a/pallets/subtensor/src/staking/move_stake.rs b/pallets/subtensor/src/staking/move_stake.rs index 81cfef289..9628c5814 100644 --- a/pallets/subtensor/src/staking/move_stake.rs +++ b/pallets/subtensor/src/staking/move_stake.rs @@ -1,5 +1,5 @@ use super::*; -// use substrate_fixed::types::I96F32; +use sp_core::Get; impl Pallet { /// Moves stake from one hotkey to another across subnets. @@ -75,6 +75,12 @@ impl Pallet { alpha_amount, ); + // Ensure origin_tao is at least DefaultMinStake + ensure!( + origin_tao >= DefaultMinStake::::get(), + Error::::AmountTooLow + ); + // --- 8. Stake the resulting TAO into the destination subnet for the destination hotkey Self::stake_into_subnet( &destination_hotkey.clone(), diff --git a/pallets/subtensor/src/tests/move_stake.rs b/pallets/subtensor/src/tests/move_stake.rs index b40ea79c4..0953a6a35 100644 --- a/pallets/subtensor/src/tests/move_stake.rs +++ b/pallets/subtensor/src/tests/move_stake.rs @@ -16,7 +16,7 @@ fn test_do_move_success() { let coldkey = U256::from(1); let origin_hotkey = U256::from(2); let destination_hotkey = U256::from(3); - let stake_amount = 1000; + let stake_amount = 1_000_000; // Set up initial stake SubtensorModule::create_account_if_non_existent(&coldkey, &origin_hotkey); @@ -54,7 +54,7 @@ fn test_do_move_success() { netuid ), stake_amount, - epsilon = 5 + epsilon = 100 ); }); } @@ -72,7 +72,7 @@ fn test_do_move_different_subnets() { let coldkey = U256::from(1); let origin_hotkey = U256::from(2); let destination_hotkey = U256::from(3); - let stake_amount = 1000; + let stake_amount = 1_000_000; // Set up initial stake and subnets SubtensorModule::create_account_if_non_existent(&coldkey, &origin_hotkey); @@ -110,7 +110,7 @@ fn test_do_move_different_subnets() { destination_netuid ), stake_amount, - epsilon = 5 + epsilon = 100 ); }); } @@ -128,7 +128,7 @@ fn test_do_move_nonexistent_subnet() { let origin_hotkey = U256::from(2); let destination_hotkey = U256::from(3); let nonexistent_netuid = 99; // Assuming this subnet doesn't exist - let stake_amount = 1000; + let stake_amount = 1_000_000; // Set up initial stake SubtensorModule::stake_into_subnet(&origin_hotkey, &coldkey, origin_netuid, stake_amount); @@ -159,7 +159,7 @@ fn test_do_move_nonexistent_subnet() { origin_netuid ), stake_amount, - epsilon = 5 + epsilon = 100 ); }); } @@ -220,7 +220,7 @@ fn test_do_move_nonexistent_destination_hotkey() { let origin_hotkey = U256::from(2); let nonexistent_destination_hotkey = U256::from(99); // Assuming this hotkey doesn't exist let netuid = 1; - let stake_amount = 1000; + let stake_amount = 1_000_000; // Set up initial stake SubtensorModule::stake_into_subnet(&origin_hotkey, &coldkey, netuid, stake_amount); @@ -234,7 +234,7 @@ fn test_do_move_nonexistent_destination_hotkey() { nonexistent_destination_hotkey, netuid, netuid, - 123 + 1234 ), Error::::HotKeyAccountNotExists ); @@ -246,7 +246,7 @@ fn test_do_move_nonexistent_destination_hotkey() { &coldkey, netuid ), - 1000 + stake_amount ); assert_eq!( SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( @@ -259,51 +259,6 @@ fn test_do_move_nonexistent_destination_hotkey() { }); } -// 7. test_do_move_zero_stake -// Description: Test moving zero stake, which should succeed but have no effect -// SKIP_WASM_BUILD=1 RUST_LOG=debug cargo test --test move -- test_do_move_zero_stake --exact --nocapture -#[test] -fn test_do_move_zero_stake() { - new_test_ext(1).execute_with(|| { - let subnet_owner_coldkey = U256::from(1001); - let subnet_owner_hotkey = U256::from(1002); - let netuid = add_dynamic_network(&subnet_owner_hotkey, &subnet_owner_coldkey); - let coldkey = U256::from(1); - let origin_hotkey = U256::from(2); - let destination_hotkey = U256::from(3); - - // Attempt to move zero stake - SubtensorModule::create_account_if_non_existent(&coldkey, &origin_hotkey); - SubtensorModule::create_account_if_non_existent(&coldkey, &destination_hotkey); - assert_ok!(SubtensorModule::do_move_stake( - RuntimeOrigin::signed(coldkey), - origin_hotkey, - destination_hotkey, - netuid, - netuid, - 0, - )); - - // Check that no stake was moved - assert_eq!( - SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( - &origin_hotkey, - &coldkey, - netuid - ), - 0 - ); - assert_eq!( - SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( - &destination_hotkey, - &coldkey, - netuid - ), - 0 - ); - }); -} - // 8. test_do_move_all_stake // Description: Test moving all stake from one hotkey to another // SKIP_WASM_BUILD=1 RUST_LOG=debug cargo test --test move -- test_do_move_all_stake --exact --nocapture @@ -316,7 +271,7 @@ fn test_do_move_all_stake() { let coldkey = U256::from(1); let origin_hotkey = U256::from(2); let destination_hotkey = U256::from(3); - let stake_amount = 1000; + let stake_amount = 1_000_000; // Set up initial stake SubtensorModule::stake_into_subnet(&origin_hotkey, &coldkey, netuid, stake_amount); @@ -354,7 +309,7 @@ fn test_do_move_all_stake() { netuid ), stake_amount, - epsilon = 5 + epsilon = 100 ); }); } @@ -368,7 +323,7 @@ fn test_do_move_half_stake() { let coldkey = U256::from(1); let origin_hotkey = U256::from(2); let destination_hotkey = U256::from(3); - let stake_amount = 1000; + let stake_amount = 1_000_000; // Set up initial stake SubtensorModule::stake_into_subnet(&origin_hotkey, &coldkey, netuid, stake_amount); @@ -398,7 +353,7 @@ fn test_do_move_half_stake() { netuid ), stake_amount / 2, - epsilon = 5 + epsilon = 100 ); assert_abs_diff_eq!( SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( @@ -407,7 +362,7 @@ fn test_do_move_half_stake() { netuid ), stake_amount / 2, - epsilon = 5 + epsilon = 100 ); }); } @@ -424,7 +379,7 @@ fn test_do_move_partial_stake() { let coldkey = U256::from(1); let origin_hotkey = U256::from(2); let destination_hotkey = U256::from(3); - let total_stake = 1000; + let total_stake = 1_000_000; // Set up initial stake SubtensorModule::stake_into_subnet(&origin_hotkey, &coldkey, netuid, total_stake); @@ -462,7 +417,7 @@ fn test_do_move_partial_stake() { netuid ), total_stake, - epsilon = 5 + epsilon = 100 ); }); } @@ -479,7 +434,7 @@ fn test_do_move_multiple_times() { let coldkey = U256::from(1); let hotkey1 = U256::from(2); let hotkey2 = U256::from(3); - let initial_stake = 1000; + let initial_stake = 1_000_000; // Set up initial stake SubtensorModule::create_account_if_non_existent(&coldkey, &hotkey1); @@ -516,7 +471,7 @@ fn test_do_move_multiple_times() { assert_abs_diff_eq!( SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet(&hotkey1, &coldkey, netuid), initial_stake, - epsilon = 10 + epsilon = 100 ); assert_eq!( SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet(&hotkey2, &coldkey, netuid), @@ -593,7 +548,7 @@ fn test_do_move_same_hotkey() { let netuid = add_dynamic_network(&subnet_owner_hotkey, &subnet_owner_coldkey); let coldkey = U256::from(1); let hotkey = U256::from(2); - let stake_amount = 1000; + let stake_amount = 1_000_000; // Set up initial stake SubtensorModule::create_account_if_non_existent(&coldkey, &hotkey); @@ -632,7 +587,7 @@ fn test_do_move_event_emission() { let coldkey = U256::from(1); let origin_hotkey = U256::from(2); let destination_hotkey = U256::from(3); - let stake_amount = 1000; + let stake_amount = 10_000; // Set up initial stake SubtensorModule::create_account_if_non_existent(&coldkey, &origin_hotkey); @@ -683,7 +638,7 @@ fn test_do_move_storage_updates() { let coldkey = U256::from(1); let origin_hotkey = U256::from(2); let destination_hotkey = U256::from(3); - let stake_amount = 1000; + let stake_amount = 1_000_000; // Set up initial stake SubtensorModule::stake_into_subnet(&origin_hotkey, &coldkey, origin_netuid, stake_amount); @@ -781,3 +736,40 @@ fn test_do_move_max_values() { ); }); } + +// Verify moving too low amount is impossible +#[test] +fn test_moving_too_little_fails() { + new_test_ext(1).execute_with(|| { + let hotkey_account_id = U256::from(533453); + let coldkey_account_id = U256::from(55453); + let amount = 10_000; + + //add network + let netuid: u16 = add_dynamic_network(&hotkey_account_id, &coldkey_account_id); + let netuid2: u16 = add_dynamic_network(&hotkey_account_id, &coldkey_account_id); + + // Give it some $$$ in his coldkey balance + SubtensorModule::add_balance_to_coldkey_account(&coldkey_account_id, amount); + + assert_ok!(SubtensorModule::add_stake( + RuntimeOrigin::signed(coldkey_account_id), + hotkey_account_id, + netuid, + amount + )); + + // Coldkey / hotkey 0 decreases take to 5%. This should fail as the minimum take is 9% + assert_err!( + SubtensorModule::move_stake( + RuntimeOrigin::signed(coldkey_account_id), + hotkey_account_id, + hotkey_account_id, + netuid, + netuid2, + 1 + ), + Error::::AmountTooLow + ); + }); +} \ No newline at end of file diff --git a/pallets/subtensor/src/tests/senate.rs b/pallets/subtensor/src/tests/senate.rs index 7fb86c5c6..3570d11f3 100644 --- a/pallets/subtensor/src/tests/senate.rs +++ b/pallets/subtensor/src/tests/senate.rs @@ -1,7 +1,7 @@ #![allow(clippy::unwrap_used)] use super::mock::*; - +use approx::assert_abs_diff_eq; use codec::Encode; use frame_support::{assert_noop, assert_ok}; use frame_system::{EventRecord, Phase}; @@ -780,22 +780,24 @@ fn test_adjust_senate_events() { <::RuntimeOrigin>::signed(coldkey_account_id), replacement_hotkey_account_id, root_netuid, - 1 // Will be more than the last one in the senate by stake (has 0 stake) + 10_000 // Will be more than the last one in the senate by stake (has 0 stake) )); - assert_eq!( + assert_abs_diff_eq!( SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( &replacement_hotkey_account_id, &coldkey_account_id, root_netuid ), - 1 + 10_000, + epsilon = 10 ); - assert_eq!( + assert_abs_diff_eq!( SubtensorModule::get_stake_for_hotkey_on_subnet( &replacement_hotkey_account_id, root_netuid ), - 1 + 10_000, + epsilon = 10 ); System::reset_events(); diff --git a/pallets/subtensor/src/tests/swap_coldkey.rs b/pallets/subtensor/src/tests/swap_coldkey.rs index a4ec2d499..d3351c597 100644 --- a/pallets/subtensor/src/tests/swap_coldkey.rs +++ b/pallets/subtensor/src/tests/swap_coldkey.rs @@ -7,6 +7,7 @@ use frame_system::{Config, RawOrigin}; use super::mock::*; use crate::*; use crate::{Call, ColdkeySwapScheduleDuration, Error}; +use approx::assert_abs_diff_eq; use frame_support::error::BadOrigin; use frame_support::traits::schedule::v3::Named as ScheduleNamed; use frame_support::traits::schedule::DispatchTime; @@ -311,7 +312,7 @@ fn test_swap_idempotency() { let new_coldkey = U256::from(2); let hotkey = U256::from(3); let netuid = 1u16; - let stake = 100; + let stake = 10_000; // Add a network add_network(netuid, 1, 0); @@ -364,7 +365,7 @@ fn test_swap_with_max_values() { let other_coldkey = U256::from(7); let netuid = 1u16; let netuid2 = 2u16; - let stake = 100; + let stake = 10_000; let max_stake = 21_000_000_000_000_000; // 21 Million TAO; max possible balance. // Add a network @@ -432,7 +433,7 @@ fn test_swap_with_non_existent_new_coldkey() { let old_coldkey = U256::from(1); let new_coldkey = U256::from(2); let hotkey = U256::from(3); - let stake = 100; + let stake = 10_000; let netuid = 1u16; add_network(netuid, 1, 0); register_ok_neuron(netuid, hotkey, old_coldkey, 1001000); @@ -495,7 +496,7 @@ fn test_swap_effect_on_delegated_stake() { let new_coldkey = U256::from(2); let delegator = U256::from(3); let hotkey = U256::from(4); - let stake = 100; + let stake = 10_000; StakingHotkeys::::insert(old_coldkey, vec![hotkey]); StakingHotkeys::::insert(delegator, vec![hotkey]); @@ -1590,13 +1591,13 @@ fn test_coldkey_delegations() { delegate )); // register on root register_ok_neuron(netuid2, delegate, owner, 0); - SubtensorModule::add_balance_to_coldkey_account(&coldkey, 1000); + SubtensorModule::add_balance_to_coldkey_account(&coldkey, 100_000); assert_ok!(SubtensorModule::add_stake( <::RuntimeOrigin>::signed(coldkey), delegate, netuid, - 100_u64 + 10_000 )); // Add stake to netuid2 @@ -1604,7 +1605,7 @@ fn test_coldkey_delegations() { <::RuntimeOrigin>::signed(coldkey), delegate, netuid2, - 100_u64 + 10_000 )); // Perform the swap @@ -1616,19 +1617,29 @@ fn test_coldkey_delegations() { )); // Verify stake was moved for the delegate - assert_eq!( + assert_abs_diff_eq!( SubtensorModule::get_total_stake_for_hotkey(&delegate), - 100 * 2 + 10_000 * 2, + epsilon = 10 ); assert_eq!(SubtensorModule::get_total_stake_for_coldkey(&coldkey), 0); - assert_eq!( + assert_abs_diff_eq!( SubtensorModule::get_total_stake_for_coldkey(&new_coldkey), - 100 * 2 + 10_000 * 2, + epsilon = 10 + ); + assert_abs_diff_eq!( + Alpha::::get((delegate, new_coldkey, netuid)).to_num::(), + 10_000, + epsilon = 10 ); - assert_eq!(Alpha::::get((delegate, new_coldkey, netuid)), 100); assert_eq!(Alpha::::get((delegate, coldkey, netuid)), 0); - assert_eq!(Alpha::::get((delegate, new_coldkey, netuid2)), 100); + assert_abs_diff_eq!( + Alpha::::get((delegate, new_coldkey, netuid2)).to_num::(), + 10_000, + epsilon = 10 + ); assert_eq!(Alpha::::get((delegate, coldkey, netuid2)), 0); }); } From e14f195e0eff89d90793b96bcd633188f115ff65 Mon Sep 17 00:00:00 2001 From: Cameron Fairchild Date: Thu, 16 Jan 2025 11:06:43 -0500 Subject: [PATCH 09/20] skip stake info entry if none staked --- pallets/subtensor/src/rpc_info/stake_info.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/pallets/subtensor/src/rpc_info/stake_info.rs b/pallets/subtensor/src/rpc_info/stake_info.rs index 2f87453f3..97afd5aa2 100644 --- a/pallets/subtensor/src/rpc_info/stake_info.rs +++ b/pallets/subtensor/src/rpc_info/stake_info.rs @@ -35,6 +35,9 @@ impl Pallet { let alpha: u64 = Self::get_stake_for_hotkey_and_coldkey_on_subnet( hotkey_i, coldkey_i, *netuid_i, ); + if alpha == 0 { + continue; + } let emission: u64 = AlphaDividendsPerSubnet::::get(*netuid_i, &hotkey_i); let is_registered: bool = Self::is_hotkey_registered_on_network(*netuid_i, hotkey_i); From 77f0375fa8885e62a01eb1bb3206c97bd7f85bb0 Mon Sep 17 00:00:00 2001 From: Greg Zaitsev Date: Thu, 16 Jan 2025 12:30:06 -0500 Subject: [PATCH 10/20] Fix remaining tests --- pallets/subtensor/src/tests/move_stake.rs | 2 +- pallets/subtensor/src/tests/swap_coldkey.rs | 54 ++++++++++----------- pallets/subtensor/src/tests/weights.rs | 6 +-- 3 files changed, 31 insertions(+), 31 deletions(-) diff --git a/pallets/subtensor/src/tests/move_stake.rs b/pallets/subtensor/src/tests/move_stake.rs index 0953a6a35..3756982af 100644 --- a/pallets/subtensor/src/tests/move_stake.rs +++ b/pallets/subtensor/src/tests/move_stake.rs @@ -772,4 +772,4 @@ fn test_moving_too_little_fails() { Error::::AmountTooLow ); }); -} \ No newline at end of file +} diff --git a/pallets/subtensor/src/tests/swap_coldkey.rs b/pallets/subtensor/src/tests/swap_coldkey.rs index d3351c597..20cd659d7 100644 --- a/pallets/subtensor/src/tests/swap_coldkey.rs +++ b/pallets/subtensor/src/tests/swap_coldkey.rs @@ -101,7 +101,7 @@ fn test_swap_total_coldkey_stake() { let other_coldkey = U256::from(3); let hotkey = U256::from(4); let other_hotkey = U256::from(5); - let stake = 100; + let stake = 10_000; let netuid = 1u16; add_network(netuid, 1, 0); @@ -1234,13 +1234,13 @@ fn test_coldkey_swap_total() { let netuid1 = 1u16; let netuid2 = 2u16; let netuid3 = 3u16; - SubtensorModule::add_balance_to_coldkey_account(&coldkey, 1000); - SubtensorModule::add_balance_to_coldkey_account(&delegate1, 1000); - SubtensorModule::add_balance_to_coldkey_account(&delegate2, 1000); - SubtensorModule::add_balance_to_coldkey_account(&delegate3, 1000); - SubtensorModule::add_balance_to_coldkey_account(&nominator1, 1000); - SubtensorModule::add_balance_to_coldkey_account(&nominator2, 1000); - SubtensorModule::add_balance_to_coldkey_account(&nominator3, 1000); + SubtensorModule::add_balance_to_coldkey_account(&coldkey, 100_000); + SubtensorModule::add_balance_to_coldkey_account(&delegate1, 100_000); + SubtensorModule::add_balance_to_coldkey_account(&delegate2, 100_000); + SubtensorModule::add_balance_to_coldkey_account(&delegate3, 100_000); + SubtensorModule::add_balance_to_coldkey_account(&nominator1, 100_000); + SubtensorModule::add_balance_to_coldkey_account(&nominator2, 100_000); + SubtensorModule::add_balance_to_coldkey_account(&nominator3, 100_000); // Setup initial state add_network(netuid1, 13, 0); @@ -1263,113 +1263,113 @@ fn test_coldkey_swap_total() { <::RuntimeOrigin>::signed(coldkey), hotkey1, netuid1, - 100 + 10_000 )); assert_ok!(SubtensorModule::add_stake( <::RuntimeOrigin>::signed(coldkey), hotkey2, netuid1, - 100 + 10_000 )); assert_ok!(SubtensorModule::add_stake( <::RuntimeOrigin>::signed(coldkey), hotkey3, netuid1, - 100 + 10_000 )); assert_ok!(SubtensorModule::add_stake( <::RuntimeOrigin>::signed(coldkey), delegate1, netuid1, - 100 + 10_000 )); assert_ok!(SubtensorModule::add_stake( <::RuntimeOrigin>::signed(coldkey), delegate2, netuid1, - 100 + 10_000 )); assert_ok!(SubtensorModule::add_stake( <::RuntimeOrigin>::signed(coldkey), delegate3, netuid1, - 100 + 10_000 )); assert_ok!(SubtensorModule::add_stake( <::RuntimeOrigin>::signed(delegate1), hotkey1, netuid1, - 100 + 10_000 )); assert_ok!(SubtensorModule::add_stake( <::RuntimeOrigin>::signed(delegate2), hotkey2, netuid1, - 100 + 10_000 )); assert_ok!(SubtensorModule::add_stake( <::RuntimeOrigin>::signed(delegate3), hotkey3, netuid1, - 100 + 10_000 )); assert_ok!(SubtensorModule::add_stake( <::RuntimeOrigin>::signed(delegate1), delegate1, netuid1, - 100 + 10_000 )); assert_ok!(SubtensorModule::add_stake( <::RuntimeOrigin>::signed(delegate2), delegate2, netuid1, - 100 + 10_000 )); assert_ok!(SubtensorModule::add_stake( <::RuntimeOrigin>::signed(delegate3), delegate3, netuid1, - 100 + 10_000 )); assert_ok!(SubtensorModule::add_stake( <::RuntimeOrigin>::signed(nominator1), hotkey1, netuid1, - 100 + 10_000 )); assert_ok!(SubtensorModule::add_stake( <::RuntimeOrigin>::signed(nominator2), hotkey2, netuid1, - 100 + 10_000 )); assert_ok!(SubtensorModule::add_stake( <::RuntimeOrigin>::signed(nominator3), hotkey3, netuid1, - 100 + 10_000 )); assert_ok!(SubtensorModule::add_stake( <::RuntimeOrigin>::signed(nominator1), delegate1, netuid1, - 100 + 10_000 )); assert_ok!(SubtensorModule::add_stake( <::RuntimeOrigin>::signed(nominator2), delegate2, netuid1, - 100 + 10_000 )); assert_ok!(SubtensorModule::add_stake( <::RuntimeOrigin>::signed(nominator3), delegate3, netuid1, - 100 + 10_000 )); assert_eq!( @@ -1714,7 +1714,7 @@ fn test_schedule_swap_coldkey_execution() { let new_coldkey = U256::from(2); let hotkey = U256::from(3); let netuid = 1u16; - let stake_amount = 100; + let stake_amount = 10_000; add_network(netuid, 13, 0); register_ok_neuron(netuid, hotkey, old_coldkey, 0); diff --git a/pallets/subtensor/src/tests/weights.rs b/pallets/subtensor/src/tests/weights.rs index 50c228dc8..4cffd59bc 100644 --- a/pallets/subtensor/src/tests/weights.rs +++ b/pallets/subtensor/src/tests/weights.rs @@ -139,7 +139,7 @@ fn test_set_rootweights_validate() { RuntimeOrigin::signed(hotkey), hotkey, netuid, - 1 + 10_000 )); // Verify stake is more than minimum @@ -253,7 +253,7 @@ fn test_commit_weights_validate() { RuntimeOrigin::signed(hotkey), hotkey, netuid, - 1 + 10_000 )); // Verify stake is more than minimum @@ -433,7 +433,7 @@ fn test_reveal_weights_validate() { RuntimeOrigin::signed(hotkey), hotkey, netuid, - 1 + 10_000 )); // Verify stake is more than minimum From c2ff7992640f52581a75b18408805016ba01b342 Mon Sep 17 00:00:00 2001 From: gztensor <166415444+gztensor@users.noreply.github.com> Date: Thu, 16 Jan 2025 09:30:31 -0800 Subject: [PATCH 11/20] Update pallets/subtensor/src/macros/events.rs Co-authored-by: Cameron Fairchild --- pallets/subtensor/src/macros/events.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pallets/subtensor/src/macros/events.rs b/pallets/subtensor/src/macros/events.rs index ff61784e7..30566ac47 100644 --- a/pallets/subtensor/src/macros/events.rs +++ b/pallets/subtensor/src/macros/events.rs @@ -17,7 +17,7 @@ mod events { StakeAdded(T::AccountId, T::AccountId, u64, u64, u16), /// stake has been removed from the hotkey staking account onto the coldkey account. StakeRemoved(T::AccountId, T::AccountId, u64, u64, u16), - /// stake has been moved from origin (hotkey, subnet ID) to destination (hotkey, subnet ID). + /// stake has been moved from origin (hotkey, subnet ID) to destination (hotkey, subnet ID) of this amount (in TAO). StakeMoved(T::AccountId, T::AccountId, u16, T::AccountId, u16, u64), /// a caller successfully sets their weights on a subnetwork. WeightsSet(u16, u16), From 8957f3d5163a5fdc6fb9541f9131394584bad9b4 Mon Sep 17 00:00:00 2001 From: Greg Zaitsev Date: Thu, 16 Jan 2025 16:18:37 -0500 Subject: [PATCH 12/20] Fix tests after raising min stake to 2_000_000 rao --- pallets/subtensor/src/lib.rs | 3 +- pallets/subtensor/src/tests/children.rs | 72 ++++++++------ pallets/subtensor/src/tests/epoch.rs | 6 +- pallets/subtensor/src/tests/move_stake.rs | 38 ++++---- pallets/subtensor/src/tests/senate.rs | 96 ++++++++++-------- pallets/subtensor/src/tests/staking.rs | 89 +++++++++-------- pallets/subtensor/src/tests/swap_coldkey.rs | 103 ++++++++++---------- pallets/subtensor/src/tests/swap_hotkey.rs | 8 +- pallets/subtensor/src/tests/weights.rs | 22 ++--- 9 files changed, 237 insertions(+), 200 deletions(-) diff --git a/pallets/subtensor/src/lib.rs b/pallets/subtensor/src/lib.rs index 320dd475c..66c248f02 100644 --- a/pallets/subtensor/src/lib.rs +++ b/pallets/subtensor/src/lib.rs @@ -703,8 +703,9 @@ pub mod pallet { #[pallet::type_value] /// Default minimum stake. + /// 2M rao matches $1 at $500/TAO pub fn DefaultMinStake() -> u64 { - 1_000 + 2_000_000 } #[pallet::type_value] diff --git a/pallets/subtensor/src/tests/children.rs b/pallets/subtensor/src/tests/children.rs index 8b270c78d..0a7c147ff 100644 --- a/pallets/subtensor/src/tests/children.rs +++ b/pallets/subtensor/src/tests/children.rs @@ -3058,28 +3058,36 @@ fn test_childkey_multiple_parents_emission() { // Register neurons and add initial stakes let initial_stakes: Vec<(bool, U256, U256, u64)> = vec![ - (false, coldkey_parent1, parent1, 200_000), - (true, coldkey_parent2, parent2, 150_000), - (true, coldkey_child, child, 20_000), - (true, coldkey_weight_setter, weight_setter, 100_000), + (false, coldkey_parent1, parent1, 200_000_000), + (true, coldkey_parent2, parent2, 150_000_000), + (true, coldkey_child, child, 20_000_000), + (true, coldkey_weight_setter, weight_setter, 100_000_000), ]; - for (register, coldkey, hotkey, stake) in initial_stakes.iter() { - SubtensorModule::add_balance_to_coldkey_account(coldkey, *stake); - if *register { - // Register a neuron - register_ok_neuron(netuid, *hotkey, *coldkey, 0); - } else { - // Just create hotkey account - SubtensorModule::create_account_if_non_existent(coldkey, hotkey); - } - assert_ok!(SubtensorModule::add_stake( - RuntimeOrigin::signed(*coldkey), - *hotkey, - netuid, - *stake - )); - } + let initial_actual_stakes: Vec = initial_stakes + .iter() + .map(|(register, coldkey, hotkey, stake)| { + SubtensorModule::add_balance_to_coldkey_account(coldkey, *stake); + if *register { + // Register a neuron + register_ok_neuron(netuid, *hotkey, *coldkey, 0); + } else { + // Just create hotkey account + SubtensorModule::create_account_if_non_existent(coldkey, hotkey); + } + assert_ok!(SubtensorModule::add_stake( + RuntimeOrigin::signed(*coldkey), + *hotkey, + netuid, + *stake + )); + + // Return actual stake + SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( + &hotkey, &coldkey, netuid, + ) + }) + .collect(); SubtensorModule::set_weights_set_rate_limit(netuid, 0); step_block(2); @@ -3152,23 +3160,26 @@ fn test_childkey_multiple_parents_emission() { ); assert!( - parent1_stake > 200_000, + parent1_stake > initial_actual_stakes[0], "Parent1 should have received emission" ); assert!( - parent2_stake > 150_000, + parent2_stake > initial_actual_stakes[1], "Parent2 should have received emission" ); - assert!(child_stake > 20_000, "Child should have received emission"); assert!( - weight_setter_stake > 100_000, + child_stake > initial_actual_stakes[2], + "Child should have received emission" + ); + assert!( + weight_setter_stake > initial_actual_stakes[3], "Weight setter should have received emission" ); // Check individual stake increases - let parent1_stake_increase = parent1_stake - 200_000; - let parent2_stake_increase = parent2_stake - 150_000; - let child_stake_increase = child_stake - 20_000; + let parent1_stake_increase = parent1_stake - initial_actual_stakes[0]; + let parent2_stake_increase = parent2_stake - initial_actual_stakes[1]; + let child_stake_increase = child_stake - initial_actual_stakes[2]; log::debug!( "Stake increases - Parent1: {}, Parent2: {}, Child: {}", @@ -3192,12 +3203,13 @@ fn test_childkey_multiple_parents_emission() { ); // Check that the total stake has increased by the emission amount + // Allow 1% slippage let total_stake = parent1_stake + parent2_stake + child_stake + weight_setter_stake; - let initial_total_stake: u64 = initial_stakes.iter().map(|(_, _, _, stake)| stake).sum(); + let initial_total_stake: u64 = initial_actual_stakes.iter().sum::(); assert_abs_diff_eq!( total_stake, - initial_total_stake + total_emission - 2, - epsilon = total_emission / 10_000 + initial_total_stake + total_emission, + epsilon = total_emission / 100 ); }); } diff --git a/pallets/subtensor/src/tests/epoch.rs b/pallets/subtensor/src/tests/epoch.rs index 0bd5a18cb..43ac6b98c 100644 --- a/pallets/subtensor/src/tests/epoch.rs +++ b/pallets/subtensor/src/tests/epoch.rs @@ -12,7 +12,7 @@ use frame_support::{assert_err, assert_ok}; // use frame_system::Config; use rand::{distributions::Uniform, rngs::StdRng, seq::SliceRandom, thread_rng, Rng, SeedableRng}; -use sp_core::U256; +use sp_core::{Get, U256}; // use sp_runtime::DispatchError; use std::time::Instant; use substrate_fixed::types::I32F32; @@ -1505,7 +1505,7 @@ fn test_set_alpha_disabled() { signer.clone(), hotkey, netuid, - 1000 + DefaultMinStake::::get() )); // Only owner can set alpha values assert_ok!(SubtensorModule::register_network(signer.clone(), hotkey)); @@ -2600,7 +2600,7 @@ fn test_get_set_alpha() { signer.clone(), hotkey, netuid, - 1000 + DefaultMinStake::::get() )); assert_ok!(SubtensorModule::do_set_alpha_values( diff --git a/pallets/subtensor/src/tests/move_stake.rs b/pallets/subtensor/src/tests/move_stake.rs index 3756982af..f26a82432 100644 --- a/pallets/subtensor/src/tests/move_stake.rs +++ b/pallets/subtensor/src/tests/move_stake.rs @@ -2,7 +2,7 @@ use super::mock::*; use crate::*; use approx::assert_abs_diff_eq; use frame_support::{assert_err, assert_noop, assert_ok}; -use sp_core::U256; +use sp_core::{Get, U256}; // 1. test_do_move_success // Description: Test a successful move of stake between two hotkeys in the same subnet @@ -16,7 +16,7 @@ fn test_do_move_success() { let coldkey = U256::from(1); let origin_hotkey = U256::from(2); let destination_hotkey = U256::from(3); - let stake_amount = 1_000_000; + let stake_amount = DefaultMinStake::::get() * 10; // Set up initial stake SubtensorModule::create_account_if_non_existent(&coldkey, &origin_hotkey); @@ -54,7 +54,7 @@ fn test_do_move_success() { netuid ), stake_amount, - epsilon = 100 + epsilon = stake_amount / 1000 ); }); } @@ -72,7 +72,7 @@ fn test_do_move_different_subnets() { let coldkey = U256::from(1); let origin_hotkey = U256::from(2); let destination_hotkey = U256::from(3); - let stake_amount = 1_000_000; + let stake_amount = DefaultMinStake::::get() * 10; // Set up initial stake and subnets SubtensorModule::create_account_if_non_existent(&coldkey, &origin_hotkey); @@ -110,7 +110,7 @@ fn test_do_move_different_subnets() { destination_netuid ), stake_amount, - epsilon = 100 + epsilon = stake_amount / 1000 ); }); } @@ -271,7 +271,7 @@ fn test_do_move_all_stake() { let coldkey = U256::from(1); let origin_hotkey = U256::from(2); let destination_hotkey = U256::from(3); - let stake_amount = 1_000_000; + let stake_amount = DefaultMinStake::::get() * 10; // Set up initial stake SubtensorModule::stake_into_subnet(&origin_hotkey, &coldkey, netuid, stake_amount); @@ -309,7 +309,7 @@ fn test_do_move_all_stake() { netuid ), stake_amount, - epsilon = 100 + epsilon = stake_amount / 1000 ); }); } @@ -323,7 +323,7 @@ fn test_do_move_half_stake() { let coldkey = U256::from(1); let origin_hotkey = U256::from(2); let destination_hotkey = U256::from(3); - let stake_amount = 1_000_000; + let stake_amount = DefaultMinStake::::get() * 10; // Set up initial stake SubtensorModule::stake_into_subnet(&origin_hotkey, &coldkey, netuid, stake_amount); @@ -353,7 +353,7 @@ fn test_do_move_half_stake() { netuid ), stake_amount / 2, - epsilon = 100 + epsilon = stake_amount / 1000 ); assert_abs_diff_eq!( SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( @@ -362,7 +362,7 @@ fn test_do_move_half_stake() { netuid ), stake_amount / 2, - epsilon = 100 + epsilon = stake_amount / 1000 ); }); } @@ -379,7 +379,7 @@ fn test_do_move_partial_stake() { let coldkey = U256::from(1); let origin_hotkey = U256::from(2); let destination_hotkey = U256::from(3); - let total_stake = 1_000_000; + let total_stake = DefaultMinStake::::get() * 10; // Set up initial stake SubtensorModule::stake_into_subnet(&origin_hotkey, &coldkey, netuid, total_stake); @@ -417,7 +417,7 @@ fn test_do_move_partial_stake() { netuid ), total_stake, - epsilon = 100 + epsilon = total_stake / 1000 ); }); } @@ -434,7 +434,7 @@ fn test_do_move_multiple_times() { let coldkey = U256::from(1); let hotkey1 = U256::from(2); let hotkey2 = U256::from(3); - let initial_stake = 1_000_000; + let initial_stake = DefaultMinStake::::get() * 10; // Set up initial stake SubtensorModule::create_account_if_non_existent(&coldkey, &hotkey1); @@ -471,7 +471,7 @@ fn test_do_move_multiple_times() { assert_abs_diff_eq!( SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet(&hotkey1, &coldkey, netuid), initial_stake, - epsilon = 100 + epsilon = initial_stake / 1000 ); assert_eq!( SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet(&hotkey2, &coldkey, netuid), @@ -548,7 +548,7 @@ fn test_do_move_same_hotkey() { let netuid = add_dynamic_network(&subnet_owner_hotkey, &subnet_owner_coldkey); let coldkey = U256::from(1); let hotkey = U256::from(2); - let stake_amount = 1_000_000; + let stake_amount = DefaultMinStake::::get() * 10; // Set up initial stake SubtensorModule::create_account_if_non_existent(&coldkey, &hotkey); @@ -587,7 +587,7 @@ fn test_do_move_event_emission() { let coldkey = U256::from(1); let origin_hotkey = U256::from(2); let destination_hotkey = U256::from(3); - let stake_amount = 10_000; + let stake_amount = DefaultMinStake::::get() * 10; // Set up initial stake SubtensorModule::create_account_if_non_existent(&coldkey, &origin_hotkey); @@ -618,7 +618,7 @@ fn test_do_move_event_emission() { netuid, destination_hotkey, netuid, - alpha, + 19999999, // Should be TAO equivalent ) .into(), ); @@ -638,7 +638,7 @@ fn test_do_move_storage_updates() { let coldkey = U256::from(1); let origin_hotkey = U256::from(2); let destination_hotkey = U256::from(3); - let stake_amount = 1_000_000; + let stake_amount = DefaultMinStake::::get() * 10; // Set up initial stake SubtensorModule::stake_into_subnet(&origin_hotkey, &coldkey, origin_netuid, stake_amount); @@ -743,7 +743,7 @@ fn test_moving_too_little_fails() { new_test_ext(1).execute_with(|| { let hotkey_account_id = U256::from(533453); let coldkey_account_id = U256::from(55453); - let amount = 10_000; + let amount = DefaultMinStake::::get(); //add network let netuid: u16 = add_dynamic_network(&hotkey_account_id, &coldkey_account_id); diff --git a/pallets/subtensor/src/tests/senate.rs b/pallets/subtensor/src/tests/senate.rs index 3570d11f3..00a1b897f 100644 --- a/pallets/subtensor/src/tests/senate.rs +++ b/pallets/subtensor/src/tests/senate.rs @@ -1,11 +1,12 @@ #![allow(clippy::unwrap_used)] use super::mock::*; +use crate::*; use approx::assert_abs_diff_eq; use codec::Encode; use frame_support::{assert_noop, assert_ok}; use frame_system::{EventRecord, Phase}; -use sp_core::{bounded_vec, H256, U256}; +use sp_core::{bounded_vec, Get, H256, U256}; use sp_runtime::{ traits::{BlakeTwo256, Hash}, BuildStorage, @@ -65,6 +66,7 @@ fn test_senate_join_works() { let hotkey_account_id = U256::from(6); let burn_cost = 1000; let coldkey_account_id = U256::from(667); // Neighbour of the beast, har har + let stake = DefaultMinStake::::get() * 100; //add network SubtensorModule::set_burn(netuid, burn_cost); @@ -95,26 +97,28 @@ fn test_senate_join_works() { Delegates::::insert(hotkey_account_id, u16::MAX / 10); let staker_coldkey = U256::from(7); - SubtensorModule::add_balance_to_coldkey_account(&staker_coldkey, 100_000); + SubtensorModule::add_balance_to_coldkey_account(&staker_coldkey, stake); assert_ok!(SubtensorModule::add_stake( <::RuntimeOrigin>::signed(staker_coldkey), hotkey_account_id, netuid, - 100_000 + stake )); - assert_eq!( + assert_abs_diff_eq!( SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( &hotkey_account_id, &staker_coldkey, netuid ), - 99_999 + stake, + epsilon = 10 ); - assert_eq!( + assert_abs_diff_eq!( SubtensorModule::get_stake_for_hotkey_on_subnet(&hotkey_account_id, netuid), - 99_999 + stake, + epsilon = 10 ); assert_ok!(SubtensorModule::root_register( @@ -166,25 +170,28 @@ fn test_senate_vote_works() { Delegates::::insert(hotkey_account_id, u16::MAX / 10); let staker_coldkey = U256::from(7); - SubtensorModule::add_balance_to_coldkey_account(&staker_coldkey, 100_000); + let stake = DefaultMinStake::::get() * 10; + SubtensorModule::add_balance_to_coldkey_account(&staker_coldkey, stake); assert_ok!(SubtensorModule::add_stake( <::RuntimeOrigin>::signed(staker_coldkey), hotkey_account_id, netuid, - 100_000 + stake )); - assert_eq!( + assert_abs_diff_eq!( SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( &hotkey_account_id, &staker_coldkey, netuid ), - 99_999 + stake, + epsilon = stake / 1000 ); - assert_eq!( + assert_abs_diff_eq!( SubtensorModule::get_stake_for_hotkey_on_subnet(&hotkey_account_id, netuid), - 99_999 + stake, + epsilon = stake / 1000 ); assert_ok!(SubtensorModule::root_register( @@ -305,6 +312,7 @@ fn test_senate_leave_works() { let hotkey_account_id = U256::from(6); let burn_cost = 1000; let coldkey_account_id = U256::from(667); // Neighbour of the beast, har har + let stake = DefaultMinStake::::get() * 10; //add network SubtensorModule::set_burn(netuid, burn_cost); @@ -335,25 +343,27 @@ fn test_senate_leave_works() { Delegates::::insert(hotkey_account_id, u16::MAX / 10); let staker_coldkey = U256::from(7); - SubtensorModule::add_balance_to_coldkey_account(&staker_coldkey, 100_000); + SubtensorModule::add_balance_to_coldkey_account(&staker_coldkey, stake); assert_ok!(SubtensorModule::add_stake( <::RuntimeOrigin>::signed(staker_coldkey), hotkey_account_id, netuid, - 100_000 + stake )); - assert_eq!( + assert_abs_diff_eq!( SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( &hotkey_account_id, &staker_coldkey, netuid ), - 99_999 + stake, + epsilon = stake / 1000 ); - assert_eq!( + assert_abs_diff_eq!( SubtensorModule::get_stake_for_hotkey_on_subnet(&hotkey_account_id, netuid), - 99_999 + stake, + epsilon = stake / 1000 ); assert_ok!(SubtensorModule::root_register( @@ -376,12 +386,13 @@ fn test_senate_leave_vote_removal() { let burn_cost = 1000; let coldkey_account_id = U256::from(667); // Neighbour of the beast, har har let coldkey_origin = <::RuntimeOrigin>::signed(coldkey_account_id); + let stake = DefaultMinStake::::get() * 10; //add network SubtensorModule::set_burn(netuid, burn_cost); add_network(netuid, tempo, 0); // Give it some $$$ in his coldkey balance - SubtensorModule::add_balance_to_coldkey_account(&coldkey_account_id, 10000); + SubtensorModule::add_balance_to_coldkey_account(&coldkey_account_id, stake); // Subscribe and check extrinsic output assert_ok!(SubtensorModule::burned_register( @@ -392,7 +403,7 @@ fn test_senate_leave_vote_removal() { // Check if balance has decreased to pay for the burn. assert_eq!( SubtensorModule::get_coldkey_balance(&coldkey_account_id), - (10000 - burn_cost) + (stake - burn_cost) ); // funds drained on reg. // Check if neuron has added to the specified network(netuid) assert_eq!(SubtensorModule::get_subnetwork_n(netuid), 1); @@ -406,25 +417,27 @@ fn test_senate_leave_vote_removal() { Delegates::::insert(hotkey_account_id, u16::MAX / 10); let staker_coldkey = U256::from(7); - SubtensorModule::add_balance_to_coldkey_account(&staker_coldkey, 100_000); + SubtensorModule::add_balance_to_coldkey_account(&staker_coldkey, stake); assert_ok!(SubtensorModule::add_stake( <::RuntimeOrigin>::signed(staker_coldkey), hotkey_account_id, netuid, - 100_000 + stake )); - assert_eq!( + assert_abs_diff_eq!( SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( &hotkey_account_id, &staker_coldkey, netuid ), - 99_999 + stake, + epsilon = 10 ); - assert_eq!( + assert_abs_diff_eq!( SubtensorModule::get_stake_for_hotkey_on_subnet(&hotkey_account_id, netuid), - 99_999 + stake, + epsilon = 10 ); assert_ok!(SubtensorModule::root_register( @@ -545,7 +558,7 @@ fn test_senate_not_leave_when_stake_removed() { Delegates::::insert(hotkey_account_id, u16::MAX / 10); let staker_coldkey = U256::from(7); - let stake_amount: u64 = 100_000; + let stake_amount: u64 = DefaultMinStake::::get() * 10; SubtensorModule::add_balance_to_coldkey_account(&staker_coldkey, stake_amount); assert_ok!(SubtensorModule::add_stake( @@ -554,17 +567,19 @@ fn test_senate_not_leave_when_stake_removed() { netuid, stake_amount )); - assert_eq!( + assert_abs_diff_eq!( SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( &hotkey_account_id, &staker_coldkey, netuid ), - stake_amount - 1 // Need to account for ED + stake_amount, + epsilon = stake_amount / 1000 ); - assert_eq!( + assert_abs_diff_eq!( SubtensorModule::get_stake_for_hotkey_on_subnet(&hotkey_account_id, netuid), - stake_amount - 1 // Need to account for ED + stake_amount, + epsilon = stake_amount / 1000 ); assert_ok!(SubtensorModule::root_register( @@ -673,10 +688,12 @@ fn test_adjust_senate_events() { let root_netuid = SubtensorModule::get_root_netuid(); let max_senate_size: u16 = SenateMaxMembers::get() as u16; - let stake_threshold: u64 = 100_000; // Give this much to every senator + let stake_threshold: u64 = DefaultMinStake::::get(); // Give this much to every senator // We will be registering MaxMembers hotkeys and two more to try a replace - let balance_to_add = 50_000 + (stake_threshold + burn_cost) * (max_senate_size + 2) as u64; + let balance_to_add = DefaultMinStake::::get() * 10 + + 50_000 + + (stake_threshold + burn_cost) * (max_senate_size + 2) as u64; let replacement_hotkey_account_id = U256::from(7); // Will be added to the senate to replace hotkey_account_id @@ -776,11 +793,12 @@ fn test_adjust_senate_events() { // as they have no stake assert!(!Senate::is_member(&replacement_hotkey_account_id)); // Add/delegate enough stake to join the senate + let stake = DefaultMinStake::::get() * 10; assert_ok!(SubtensorModule::add_stake( <::RuntimeOrigin>::signed(coldkey_account_id), replacement_hotkey_account_id, root_netuid, - 10_000 // Will be more than the last one in the senate by stake (has 0 stake) + stake // Will be more than the last one in the senate by stake (has 0 stake) )); assert_abs_diff_eq!( SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( @@ -788,16 +806,16 @@ fn test_adjust_senate_events() { &coldkey_account_id, root_netuid ), - 10_000, - epsilon = 10 + stake, + epsilon = stake / 1000 ); assert_abs_diff_eq!( SubtensorModule::get_stake_for_hotkey_on_subnet( &replacement_hotkey_account_id, root_netuid ), - 10_000, - epsilon = 10 + stake, + epsilon = stake / 1000 ); System::reset_events(); diff --git a/pallets/subtensor/src/tests/staking.rs b/pallets/subtensor/src/tests/staking.rs index 8104fe0fa..6594887b9 100644 --- a/pallets/subtensor/src/tests/staking.rs +++ b/pallets/subtensor/src/tests/staking.rs @@ -9,7 +9,7 @@ use crate::*; use approx::assert_abs_diff_eq; use frame_support::dispatch::{DispatchClass, DispatchInfo, GetDispatchInfo, Pays}; use frame_support::sp_runtime::DispatchError; -use sp_core::{H256, U256}; +use sp_core::{Get, H256, U256}; /*********************************************************** staking::add_stake() tests @@ -41,7 +41,7 @@ fn test_add_stake_ok_no_emission() { new_test_ext(1).execute_with(|| { let hotkey_account_id = U256::from(533453); let coldkey_account_id = U256::from(55453); - let amount = 10_000; + let amount = DefaultMinStake::::get() * 10; //add network let netuid: u16 = add_dynamic_network(&hotkey_account_id, &coldkey_account_id); @@ -70,14 +70,14 @@ fn test_add_stake_ok_no_emission() { assert_abs_diff_eq!( SubtensorModule::get_total_stake_for_hotkey(&hotkey_account_id), amount, - epsilon = 1, + epsilon = amount / 1000, ); // Check if balance has decreased assert_eq!(SubtensorModule::get_coldkey_balance(&coldkey_account_id), 1); // Check if total stake has increased accordingly. - assert_eq!(SubtensorModule::get_total_stake(), 10000); + assert_eq!(SubtensorModule::get_total_stake(), amount); assert_abs_diff_eq!(SubtensorModule::get_total_stake(), amount, epsilon = 1,); }); } @@ -179,16 +179,17 @@ fn test_add_stake_ok_neuron_does_not_belong_to_coldkey() { let hotkey_id = U256::from(54544); let other_cold_key = U256::from(99498); let netuid: u16 = add_dynamic_network(&hotkey_id, &coldkey_id); + let stake = DefaultMinStake::::get() * 10; // Give it some $$$ in his coldkey balance - SubtensorModule::add_balance_to_coldkey_account(&other_cold_key, 100000); + SubtensorModule::add_balance_to_coldkey_account(&other_cold_key, stake); // Perform the request which is signed by a different cold key assert_ok!(SubtensorModule::add_stake( RuntimeOrigin::signed(other_cold_key), hotkey_id, netuid, - 1000, + stake, )); }); } @@ -198,7 +199,7 @@ fn test_add_stake_err_not_enough_belance() { new_test_ext(1).execute_with(|| { let coldkey_id = U256::from(544); let hotkey_id = U256::from(54544); - let stake = 60_000; + let stake = DefaultMinStake::::get() * 10; let netuid: u16 = add_dynamic_network(&hotkey_id, &coldkey_id); // Lets try to stake with 0 balance in cold key account @@ -561,7 +562,7 @@ fn test_remove_stake_total_issuance_no_change() { let subnet_owner_hotkey = U256::from(2); let hotkey_account_id = U256::from(581337); let coldkey_account_id = U256::from(81337); - let amount = 10_000; + let amount = DefaultMinStake::::get() * 10; let netuid: u16 = add_dynamic_network(&subnet_owner_hotkey, &subnet_owner_coldkey); register_ok_neuron(netuid, hotkey_account_id, coldkey_account_id, 192213123); @@ -593,7 +594,11 @@ fn test_remove_stake_total_issuance_no_change() { let total_issuance_after_stake = Balances::total_issuance(); // Remove all stake - let stake = SubtensorModule::get_total_stake_for_hotkey(&hotkey_account_id); + let stake = SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( + &hotkey_account_id, + &coldkey_account_id, + netuid, + ); assert_ok!(SubtensorModule::remove_stake( RuntimeOrigin::signed(coldkey_account_id), hotkey_account_id, @@ -1102,6 +1107,7 @@ fn test_clear_small_nominations() { let cold1 = U256::from(3); let cold2 = U256::from(4); let netuid: u16 = add_dynamic_network(&subnet_owner_hotkey, &subnet_owner_coldkey); + let amount = DefaultMinStake::::get() * 10; // Register hot1. register_ok_neuron(netuid, hot1, cold1, 0); @@ -1114,12 +1120,12 @@ fn test_clear_small_nominations() { assert_eq!(SubtensorModule::get_owning_coldkey_for_hotkey(&hot2), cold2); // Add stake cold1 --> hot1 (non delegation.) - SubtensorModule::add_balance_to_coldkey_account(&cold1, 1_000); + SubtensorModule::add_balance_to_coldkey_account(&cold1, amount); assert_ok!(SubtensorModule::add_stake( RuntimeOrigin::signed(cold1), hot1, netuid, - 1_000 + amount )); assert_ok!(SubtensorModule::remove_stake( RuntimeOrigin::signed(cold1), @@ -1133,12 +1139,12 @@ fn test_clear_small_nominations() { ); // Add stake cold2 --> hot1 (is delegation.) - SubtensorModule::add_balance_to_coldkey_account(&cold2, 1_000); + SubtensorModule::add_balance_to_coldkey_account(&cold2, amount); assert_ok!(SubtensorModule::add_stake( RuntimeOrigin::signed(cold2), hot1, netuid, - 1_000 + amount )); assert_ok!(SubtensorModule::remove_stake( RuntimeOrigin::signed(cold2), @@ -1152,12 +1158,12 @@ fn test_clear_small_nominations() { ); // Add stake cold1 --> hot2 (non delegation.) - SubtensorModule::add_balance_to_coldkey_account(&cold1, 1_000); + SubtensorModule::add_balance_to_coldkey_account(&cold1, amount); assert_ok!(SubtensorModule::add_stake( RuntimeOrigin::signed(cold1), hot2, netuid, - 1_000 + amount )); assert_ok!(SubtensorModule::remove_stake( RuntimeOrigin::signed(cold1), @@ -1172,12 +1178,12 @@ fn test_clear_small_nominations() { let balance1_before_cleaning = Balances::free_balance(cold1); // Add stake cold2 --> hot2 (is delegation.) - SubtensorModule::add_balance_to_coldkey_account(&cold2, 1_000); + SubtensorModule::add_balance_to_coldkey_account(&cold2, amount); assert_ok!(SubtensorModule::add_stake( RuntimeOrigin::signed(cold2), hot2, netuid, - 1_000 + amount )); assert_ok!(SubtensorModule::remove_stake( RuntimeOrigin::signed(cold2), @@ -1580,8 +1586,8 @@ fn test_get_total_delegated_stake_after_unstaking() { let delegate_coldkey = U256::from(1); let delegate_hotkey = U256::from(2); let delegator = U256::from(3); - let initial_stake = 2_000; - let unstake_amount = 500; + let initial_stake = DefaultMinStake::::get() * 10; + let unstake_amount = DefaultMinStake::::get() * 5; let existential_deposit = ExistentialDeposit::get(); let netuid = add_dynamic_network(&subnet_owner_hotkey, &subnet_owner_coldkey); @@ -1602,12 +1608,12 @@ fn test_get_total_delegated_stake_after_unstaking() { assert_abs_diff_eq!( SubtensorModule::get_total_stake_for_coldkey(&delegator), initial_stake - existential_deposit, - epsilon = 1, + epsilon = initial_stake / 1000, ); assert_abs_diff_eq!( SubtensorModule::get_total_stake_for_hotkey(&delegate_hotkey), initial_stake - existential_deposit, - epsilon = 1, + epsilon = initial_stake / 1000, ); // Unstake part of the delegation @@ -1635,12 +1641,12 @@ fn test_get_total_delegated_stake_after_unstaking() { assert_abs_diff_eq!( SubtensorModule::get_total_stake_for_coldkey(&delegator), expected_delegated_stake, - epsilon = 1, + epsilon = expected_delegated_stake / 1000, ); assert_abs_diff_eq!( SubtensorModule::get_total_stake_for_hotkey(&delegate_hotkey), expected_delegated_stake, - epsilon = 1, + epsilon = expected_delegated_stake / 1000, ); }); } @@ -1668,7 +1674,7 @@ fn test_get_total_delegated_stake_single_delegator() { let delegate_coldkey = U256::from(1); let delegate_hotkey = U256::from(2); let delegator = U256::from(3); - let stake_amount = 1_999; + let stake_amount = DefaultMinStake::::get() * 10 - 1; let existential_deposit = ExistentialDeposit::get(); let netuid = add_dynamic_network(&subnet_owner_hotkey, &subnet_owner_coldkey); @@ -1706,12 +1712,12 @@ fn test_get_total_delegated_stake_single_delegator() { assert_abs_diff_eq!( actual_delegated_stake, expected_delegated_stake, - epsilon = 1, + epsilon = expected_delegated_stake / 1000, ); assert_abs_diff_eq!( actual_delegator_stake, expected_delegated_stake, - epsilon = 1, + epsilon = expected_delegated_stake / 1000, ); }); } @@ -1726,8 +1732,8 @@ fn test_get_alpha_share_stake_multiple_delegators() { let coldkey1 = U256::from(3); let coldkey2 = U256::from(4); let existential_deposit = 2; - let stake1 = 1000; - let stake2 = 1999; + let stake1 = DefaultMinStake::::get() * 10; + let stake2 = DefaultMinStake::::get() * 10 - 1; let netuid = add_dynamic_network(&subnet_owner_hotkey, &subnet_owner_coldkey); register_ok_neuron(netuid, hotkey1, coldkey1, 0); @@ -1754,19 +1760,26 @@ fn test_get_alpha_share_stake_multiple_delegators() { // Debug prints println!("Delegator1 stake: {}", stake1); println!("Delegator2 stake: {}", stake2); - println!("Alpha share for for 1: {}", SubtensorModule::get_alpha_share_pool(hotkey1, netuid).get_value(&coldkey1)); - println!("Alpha share for for 2: {}", SubtensorModule::get_alpha_share_pool(hotkey2, netuid).get_value(&coldkey2)); + println!( + "Alpha share for for 1: {}", + SubtensorModule::get_alpha_share_pool(hotkey1, netuid).get_value(&coldkey1) + ); + println!( + "Alpha share for for 2: {}", + SubtensorModule::get_alpha_share_pool(hotkey2, netuid).get_value(&coldkey2) + ); // Calculate expected total delegated stake let expected_total_stake = stake1 + stake2 - existential_deposit * 2; - let actual_total_stake = SubtensorModule::get_alpha_share_pool(hotkey1, netuid).get_value(&coldkey1) + SubtensorModule::get_alpha_share_pool(hotkey2, netuid).get_value(&coldkey2); + let actual_total_stake = SubtensorModule::get_alpha_share_pool(hotkey1, netuid) + .get_value(&coldkey1) + + SubtensorModule::get_alpha_share_pool(hotkey2, netuid).get_value(&coldkey2); - assert_eq!( + // Total subnet stake should match the sum of delegators' stakes minus existential deposits. + assert_abs_diff_eq!( actual_total_stake, expected_total_stake, - "Total subnet stake should match the sum of delegators' stakes minus existential deposits. Expected: {}, Actual: {}", - expected_total_stake, - actual_total_stake + epsilon = expected_total_stake / 1000 ); }); } @@ -1777,8 +1790,8 @@ fn test_get_total_delegated_stake_exclude_owner_stake() { let delegate_coldkey = U256::from(1); let delegate_hotkey = U256::from(2); let delegator = U256::from(3); - let owner_stake = 1_000_000; - let delegator_stake = 999_999; + let owner_stake = DefaultMinStake::::get() * 10; + let delegator_stake = DefaultMinStake::::get() * 10 - 1; let netuid = add_dynamic_network(&delegate_hotkey, &delegate_coldkey); @@ -1819,7 +1832,7 @@ fn test_get_total_delegated_stake_exclude_owner_stake() { assert_abs_diff_eq!( actual_delegated_stake, expected_delegated_stake, - epsilon = 100 + epsilon = expected_delegated_stake / 1000 ); }); } diff --git a/pallets/subtensor/src/tests/swap_coldkey.rs b/pallets/subtensor/src/tests/swap_coldkey.rs index 20cd659d7..0cbcecfaf 100644 --- a/pallets/subtensor/src/tests/swap_coldkey.rs +++ b/pallets/subtensor/src/tests/swap_coldkey.rs @@ -12,8 +12,7 @@ use frame_support::error::BadOrigin; use frame_support::traits::schedule::v3::Named as ScheduleNamed; use frame_support::traits::schedule::DispatchTime; use frame_support::traits::OnInitialize; -use sp_core::H256; -use sp_core::U256; +use sp_core::{Get, H256, U256}; use sp_runtime::DispatchError; // // SKIP_WASM_BUILD=1 RUST_LOG=debug cargo test --test swap_coldkey -- test_swap_total_hotkey_coldkey_stakes_this_interval --exact --nocapture @@ -101,7 +100,7 @@ fn test_swap_total_coldkey_stake() { let other_coldkey = U256::from(3); let hotkey = U256::from(4); let other_hotkey = U256::from(5); - let stake = 10_000; + let stake = DefaultMinStake::::get() * 10; let netuid = 1u16; add_network(netuid, 1, 0); @@ -312,7 +311,7 @@ fn test_swap_idempotency() { let new_coldkey = U256::from(2); let hotkey = U256::from(3); let netuid = 1u16; - let stake = 10_000; + let stake = DefaultMinStake::::get() * 10; // Add a network add_network(netuid, 1, 0); @@ -433,7 +432,7 @@ fn test_swap_with_non_existent_new_coldkey() { let old_coldkey = U256::from(1); let new_coldkey = U256::from(2); let hotkey = U256::from(3); - let stake = 10_000; + let stake = DefaultMinStake::::get() * 10; let netuid = 1u16; add_network(netuid, 1, 0); register_ok_neuron(netuid, hotkey, old_coldkey, 1001000); @@ -625,8 +624,8 @@ fn test_do_swap_coldkey_success() { let hotkey1 = U256::from(3); let hotkey2 = U256::from(4); let netuid = 1u16; - let stake_amount1 = 1000u64; - let stake_amount2 = 2000u64; + let stake_amount1 = DefaultMinStake::::get() * 10; + let stake_amount2 = DefaultMinStake::::get() * 20; let swap_cost = SubtensorModule::get_key_swap_cost(); let free_balance_old = 12345u64 + swap_cost; @@ -797,9 +796,9 @@ fn test_swap_stake_for_coldkey() { let new_coldkey = U256::from(2); let hotkey1 = U256::from(3); let hotkey2 = U256::from(4); - let stake_amount1 = 1000u64; - let stake_amount2 = 2000u64; - let stake_amount3 = 3000u64; + let stake_amount1 = DefaultMinStake::::get() * 10; + let stake_amount2 = DefaultMinStake::::get() * 20; + let stake_amount3 = DefaultMinStake::::get() * 30; let total_stake = stake_amount1 + stake_amount2; let mut weight = Weight::zero(); @@ -953,8 +952,8 @@ fn test_swap_staking_hotkeys_for_coldkey() { let other_coldkey = U256::from(3); let hotkey1 = U256::from(4); let hotkey2 = U256::from(5); - let stake_amount1 = 1000u64; - let stake_amount2 = 2000u64; + let stake_amount1 = DefaultMinStake::::get() * 10; + let stake_amount2 = DefaultMinStake::::get() * 20; let total_stake = stake_amount1 + stake_amount2; let mut weight = Weight::zero(); @@ -1024,8 +1023,8 @@ fn test_swap_delegated_stake_for_coldkey() { let other_coldkey = U256::from(3); let hotkey1 = U256::from(4); let hotkey2 = U256::from(5); - let stake_amount1 = 1000u64; - let stake_amount2 = 2000u64; + let stake_amount1 = DefaultMinStake::::get() * 10; + let stake_amount2 = DefaultMinStake::::get() * 20; let mut weight = Weight::zero(); let netuid = 1u16; @@ -1234,13 +1233,14 @@ fn test_coldkey_swap_total() { let netuid1 = 1u16; let netuid2 = 2u16; let netuid3 = 3u16; - SubtensorModule::add_balance_to_coldkey_account(&coldkey, 100_000); - SubtensorModule::add_balance_to_coldkey_account(&delegate1, 100_000); - SubtensorModule::add_balance_to_coldkey_account(&delegate2, 100_000); - SubtensorModule::add_balance_to_coldkey_account(&delegate3, 100_000); - SubtensorModule::add_balance_to_coldkey_account(&nominator1, 100_000); - SubtensorModule::add_balance_to_coldkey_account(&nominator2, 100_000); - SubtensorModule::add_balance_to_coldkey_account(&nominator3, 100_000); + let stake = DefaultMinStake::::get() * 10; + SubtensorModule::add_balance_to_coldkey_account(&coldkey, stake * 6); + SubtensorModule::add_balance_to_coldkey_account(&delegate1, stake * 2); + SubtensorModule::add_balance_to_coldkey_account(&delegate2, stake * 2); + SubtensorModule::add_balance_to_coldkey_account(&delegate3, stake * 2); + SubtensorModule::add_balance_to_coldkey_account(&nominator1, stake * 2); + SubtensorModule::add_balance_to_coldkey_account(&nominator2, stake * 2); + SubtensorModule::add_balance_to_coldkey_account(&nominator3, stake * 2); // Setup initial state add_network(netuid1, 13, 0); @@ -1263,113 +1263,113 @@ fn test_coldkey_swap_total() { <::RuntimeOrigin>::signed(coldkey), hotkey1, netuid1, - 10_000 + stake )); assert_ok!(SubtensorModule::add_stake( <::RuntimeOrigin>::signed(coldkey), hotkey2, netuid1, - 10_000 + stake )); assert_ok!(SubtensorModule::add_stake( <::RuntimeOrigin>::signed(coldkey), hotkey3, netuid1, - 10_000 + stake )); assert_ok!(SubtensorModule::add_stake( <::RuntimeOrigin>::signed(coldkey), delegate1, netuid1, - 10_000 + stake )); assert_ok!(SubtensorModule::add_stake( <::RuntimeOrigin>::signed(coldkey), delegate2, netuid1, - 10_000 + stake )); assert_ok!(SubtensorModule::add_stake( <::RuntimeOrigin>::signed(coldkey), delegate3, netuid1, - 10_000 + stake )); assert_ok!(SubtensorModule::add_stake( <::RuntimeOrigin>::signed(delegate1), hotkey1, netuid1, - 10_000 + stake )); assert_ok!(SubtensorModule::add_stake( <::RuntimeOrigin>::signed(delegate2), hotkey2, netuid1, - 10_000 + stake )); assert_ok!(SubtensorModule::add_stake( <::RuntimeOrigin>::signed(delegate3), hotkey3, netuid1, - 10_000 + stake )); assert_ok!(SubtensorModule::add_stake( <::RuntimeOrigin>::signed(delegate1), delegate1, netuid1, - 10_000 + stake )); assert_ok!(SubtensorModule::add_stake( <::RuntimeOrigin>::signed(delegate2), delegate2, netuid1, - 10_000 + stake )); assert_ok!(SubtensorModule::add_stake( <::RuntimeOrigin>::signed(delegate3), delegate3, netuid1, - 10_000 + stake )); assert_ok!(SubtensorModule::add_stake( <::RuntimeOrigin>::signed(nominator1), hotkey1, netuid1, - 10_000 + stake )); assert_ok!(SubtensorModule::add_stake( <::RuntimeOrigin>::signed(nominator2), hotkey2, netuid1, - 10_000 + stake )); assert_ok!(SubtensorModule::add_stake( <::RuntimeOrigin>::signed(nominator3), hotkey3, netuid1, - 10_000 + stake )); assert_ok!(SubtensorModule::add_stake( <::RuntimeOrigin>::signed(nominator1), delegate1, netuid1, - 10_000 + stake )); assert_ok!(SubtensorModule::add_stake( <::RuntimeOrigin>::signed(nominator2), delegate2, netuid1, - 10_000 + stake )); assert_ok!(SubtensorModule::add_stake( <::RuntimeOrigin>::signed(nominator3), delegate3, netuid1, - 10_000 + stake )); assert_eq!( @@ -1582,6 +1582,7 @@ fn test_coldkey_delegations() { let delegate = U256::from(2); let netuid = 0u16; // Stake to 0 let netuid2 = 1u16; // Stake to 1 + let stake = DefaultMinStake::::get() * 10; add_network(netuid, 13, 0); // root add_network(netuid2, 13, 0); @@ -1591,13 +1592,13 @@ fn test_coldkey_delegations() { delegate )); // register on root register_ok_neuron(netuid2, delegate, owner, 0); - SubtensorModule::add_balance_to_coldkey_account(&coldkey, 100_000); + SubtensorModule::add_balance_to_coldkey_account(&coldkey, stake * 10); assert_ok!(SubtensorModule::add_stake( <::RuntimeOrigin>::signed(coldkey), delegate, netuid, - 10_000 + stake )); // Add stake to netuid2 @@ -1605,7 +1606,7 @@ fn test_coldkey_delegations() { <::RuntimeOrigin>::signed(coldkey), delegate, netuid2, - 10_000 + stake )); // Perform the swap @@ -1619,26 +1620,26 @@ fn test_coldkey_delegations() { // Verify stake was moved for the delegate assert_abs_diff_eq!( SubtensorModule::get_total_stake_for_hotkey(&delegate), - 10_000 * 2, - epsilon = 10 + stake * 2, + epsilon = stake / 1000 ); assert_eq!(SubtensorModule::get_total_stake_for_coldkey(&coldkey), 0); assert_abs_diff_eq!( SubtensorModule::get_total_stake_for_coldkey(&new_coldkey), - 10_000 * 2, - epsilon = 10 + stake * 2, + epsilon = stake / 1000 ); assert_abs_diff_eq!( Alpha::::get((delegate, new_coldkey, netuid)).to_num::(), - 10_000, - epsilon = 10 + stake, + epsilon = stake / 1000 ); assert_eq!(Alpha::::get((delegate, coldkey, netuid)), 0); assert_abs_diff_eq!( Alpha::::get((delegate, new_coldkey, netuid2)).to_num::(), - 10_000, - epsilon = 10 + stake, + epsilon = stake / 1000 ); assert_eq!(Alpha::::get((delegate, coldkey, netuid2)), 0); }); @@ -1714,7 +1715,7 @@ fn test_schedule_swap_coldkey_execution() { let new_coldkey = U256::from(2); let hotkey = U256::from(3); let netuid = 1u16; - let stake_amount = 10_000; + let stake_amount = DefaultMinStake::::get() * 10; add_network(netuid, 13, 0); register_ok_neuron(netuid, hotkey, old_coldkey, 0); diff --git a/pallets/subtensor/src/tests/swap_hotkey.rs b/pallets/subtensor/src/tests/swap_hotkey.rs index 596571a73..0f11dfa98 100644 --- a/pallets/subtensor/src/tests/swap_hotkey.rs +++ b/pallets/subtensor/src/tests/swap_hotkey.rs @@ -64,7 +64,7 @@ fn test_swap_total_hotkey_stake() { let old_hotkey = U256::from(1); let new_hotkey = U256::from(2); let coldkey = U256::from(3); - let amount = 10_000; + let amount = DefaultMinStake::::get() * 10; let mut weight = Weight::zero(); //add network @@ -85,7 +85,7 @@ fn test_swap_total_hotkey_stake() { assert_abs_diff_eq!( SubtensorModule::get_total_stake_for_hotkey(&old_hotkey), amount, - epsilon = 1, + epsilon = amount / 1000, ); assert_abs_diff_eq!( SubtensorModule::get_total_stake_for_hotkey(&new_hotkey), @@ -110,7 +110,7 @@ fn test_swap_total_hotkey_stake() { assert_abs_diff_eq!( SubtensorModule::get_total_stake_for_hotkey(&new_hotkey), amount, - epsilon = 1, + epsilon = amount / 1000, ); }); } @@ -634,7 +634,7 @@ fn test_swap_hotkey_with_multiple_coldkeys_and_subnets() { let coldkey2 = U256::from(4); let netuid1 = 1; let netuid2 = 2; - let stake = 1_000_000_u64; + let stake = DefaultMinStake::::get() * 10; let mut weight = Weight::zero(); // Set up initial state diff --git a/pallets/subtensor/src/tests/weights.rs b/pallets/subtensor/src/tests/weights.rs index 4cffd59bc..b71093776 100644 --- a/pallets/subtensor/src/tests/weights.rs +++ b/pallets/subtensor/src/tests/weights.rs @@ -1,25 +1,17 @@ #![allow(clippy::indexing_slicing)] use super::mock::*; -use crate::{ - // coinbase::run_coinbase::WeightsTlockPayload, CRV3WeightCommits, Error, Owner, - coinbase::run_coinbase::WeightsTlockPayload, - CRV3WeightCommits, - Error, - MAX_CRV3_COMMIT_SIZE_BYTES, -}; +use crate::coinbase::run_coinbase::WeightsTlockPayload; +use crate::*; use ark_serialize::CanonicalDeserialize; use frame_support::{ - assert_err, - assert_ok, - // dispatch::{DispatchClass, DispatchInfo, DispatchResult, GetDispatchInfo, Pays}, + assert_err, assert_ok, dispatch::{DispatchClass, DispatchResult, GetDispatchInfo, Pays}, - // pallet_prelude::{InvalidTransaction, TransactionValidityError}, }; use rand_chacha::{rand_core::SeedableRng, ChaCha20Rng}; use scale_info::prelude::collections::HashMap; use sha2::Digest; -use sp_core::{H256, U256}; +use sp_core::{Get, H256, U256}; use sp_runtime::{ traits::{BlakeTwo256, ConstU32, Hash, SignedExtension}, BoundedVec, DispatchError, @@ -139,7 +131,7 @@ fn test_set_rootweights_validate() { RuntimeOrigin::signed(hotkey), hotkey, netuid, - 10_000 + DefaultMinStake::::get() * 10 )); // Verify stake is more than minimum @@ -253,7 +245,7 @@ fn test_commit_weights_validate() { RuntimeOrigin::signed(hotkey), hotkey, netuid, - 10_000 + DefaultMinStake::::get() * 10 )); // Verify stake is more than minimum @@ -433,7 +425,7 @@ fn test_reveal_weights_validate() { RuntimeOrigin::signed(hotkey), hotkey, netuid, - 10_000 + DefaultMinStake::::get() * 10 )); // Verify stake is more than minimum From 16c176eca17204d2d9abf987c8396e2f9f2187eb Mon Sep 17 00:00:00 2001 From: Greg Zaitsev Date: Thu, 16 Jan 2025 16:36:54 -0500 Subject: [PATCH 13/20] Fix clippy --- pallets/subtensor/src/tests/children.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pallets/subtensor/src/tests/children.rs b/pallets/subtensor/src/tests/children.rs index 0a7c147ff..a7fef1f81 100644 --- a/pallets/subtensor/src/tests/children.rs +++ b/pallets/subtensor/src/tests/children.rs @@ -3084,7 +3084,7 @@ fn test_childkey_multiple_parents_emission() { // Return actual stake SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( - &hotkey, &coldkey, netuid, + hotkey, coldkey, netuid, ) }) .collect(); From acffcfa624dfef41631e3c63f6568b40dcdc1816 Mon Sep 17 00:00:00 2001 From: Sam Johnson Date: Thu, 16 Jan 2025 16:52:14 -0500 Subject: [PATCH 14/20] fix --- pallets/subtensor/src/tests/children.rs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/pallets/subtensor/src/tests/children.rs b/pallets/subtensor/src/tests/children.rs index a7fef1f81..eb299d88e 100644 --- a/pallets/subtensor/src/tests/children.rs +++ b/pallets/subtensor/src/tests/children.rs @@ -3083,9 +3083,7 @@ fn test_childkey_multiple_parents_emission() { )); // Return actual stake - SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( - hotkey, coldkey, netuid, - ) + SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet(hotkey, coldkey, netuid) }) .collect(); From 22d8091691b2064616d9da3bc6c92a98f7e528ef Mon Sep 17 00:00:00 2001 From: Greg Zaitsev Date: Thu, 16 Jan 2025 17:04:50 -0500 Subject: [PATCH 15/20] Remove DefaultChildkeysMinStake --- pallets/subtensor/src/lib.rs | 6 ------ 1 file changed, 6 deletions(-) diff --git a/pallets/subtensor/src/lib.rs b/pallets/subtensor/src/lib.rs index c895a7e1d..23251e512 100644 --- a/pallets/subtensor/src/lib.rs +++ b/pallets/subtensor/src/lib.rs @@ -695,12 +695,6 @@ pub mod pallet { 7200 } - #[pallet::type_value] - /// Default minimum stake for setting childkeys. - pub fn DefaultChildkeysMinStake() -> u64 { - 1_000_000_000_000 - } - #[pallet::type_value] /// Default unicode vector for tau symbol. pub fn DefaultUnicodeVecU8() -> Vec { From f5a6463c0a24e663bd126b2596dadf3afe60959a Mon Sep 17 00:00:00 2001 From: Sam Johnson Date: Thu, 16 Jan 2025 17:36:14 -0500 Subject: [PATCH 16/20] add fix_rust.sh script --- scripts/fix_rust.sh | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) create mode 100755 scripts/fix_rust.sh diff --git a/scripts/fix_rust.sh b/scripts/fix_rust.sh new file mode 100755 index 000000000..ce10a4c6d --- /dev/null +++ b/scripts/fix_rust.sh @@ -0,0 +1,28 @@ +#!/bin/sh + +set -e # Exit immediately if a command exits with a non-zero status. + +# Function to check for git changes and commit if necessary. +commit_if_changes() { + if [ -n "$(git status --porcelain)" ]; then + echo "changes detected, committing..." + git commit --allow-empty -am "$1" + echo "commit created." + fi +} + +# Step 1: Run cargo check and commit changes to Cargo.lock if any. +cargo check --workspace +commit_if_changes "commit Cargo.lock" + +# Step 2: Run cargo clippy with fixes and commit changes if any. +cargo clippy --fix --workspace --all-features +commit_if_changes "cargo clippy" + +# Step 3: Run cargo fix and commit changes if any. +cargo fix --workspace --all-features --all-targets +commit_if_changes "cargo fix" + +# Step 4: Run cargo fmt and commit changes if any. +cargo fmt +commit_if_changes "cargo fmt" From 8482e2733ab4b536844c39fc9d24555a63c7857d Mon Sep 17 00:00:00 2001 From: Sam Johnson Date: Thu, 16 Jan 2025 18:16:27 -0500 Subject: [PATCH 17/20] remove --allow-empty --- scripts/fix_rust.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/fix_rust.sh b/scripts/fix_rust.sh index ce10a4c6d..9d2af2904 100755 --- a/scripts/fix_rust.sh +++ b/scripts/fix_rust.sh @@ -6,7 +6,7 @@ set -e # Exit immediately if a command exits with a non-zero status. commit_if_changes() { if [ -n "$(git status --porcelain)" ]; then echo "changes detected, committing..." - git commit --allow-empty -am "$1" + git commit -am "$1" echo "commit created." fi } From 1c0ee691649499fe9ae04704c89121749bcd56b5 Mon Sep 17 00:00:00 2001 From: Andreea Popescu Date: Wed, 15 Jan 2025 12:18:43 +0000 Subject: [PATCH 18/20] add bonds_penalty hyperparam --- pallets/admin-utils/src/tests/mock.rs | 2 + pallets/admin-utils/src/tests/mod.rs | 33 ++++ pallets/subtensor/src/epoch/math.rs | 73 +++++++ pallets/subtensor/src/epoch/run_epoch.rs | 53 +++-- pallets/subtensor/src/lib.rs | 9 + pallets/subtensor/src/macros/config.rs | 3 + pallets/subtensor/src/macros/events.rs | 2 + pallets/subtensor/src/tests/epoch.rs | 221 +++++++++++---------- pallets/subtensor/src/tests/math.rs | 234 +++++++++++++++++++++++ pallets/subtensor/src/tests/mock.rs | 2 + pallets/subtensor/src/utils/misc.rs | 8 + runtime/src/lib.rs | 2 + 12 files changed, 528 insertions(+), 114 deletions(-) diff --git a/pallets/admin-utils/src/tests/mock.rs b/pallets/admin-utils/src/tests/mock.rs index b2dbb66c0..5517196f1 100644 --- a/pallets/admin-utils/src/tests/mock.rs +++ b/pallets/admin-utils/src/tests/mock.rs @@ -86,6 +86,7 @@ parameter_types! { pub const InitialImmunityPeriod: u16 = 2; pub const InitialMaxAllowedUids: u16 = 2; pub const InitialBondsMovingAverage: u64 = 900_000; + pub const InitialBondsPenalty: u16 = 0; pub const InitialStakePruningMin: u16 = 0; pub const InitialFoundationDistribution: u64 = 0; pub const InitialDefaultDelegateTake: u16 = 11_796; // 18% honest number. @@ -163,6 +164,7 @@ impl pallet_subtensor::Config for Test { type InitialMaxRegistrationsPerBlock = InitialMaxRegistrationsPerBlock; type InitialPruningScore = InitialPruningScore; type InitialBondsMovingAverage = InitialBondsMovingAverage; + type InitialBondsPenalty = InitialBondsPenalty; type InitialMaxAllowedValidators = InitialMaxAllowedValidators; type InitialDefaultDelegateTake = InitialDefaultDelegateTake; type InitialMinDelegateTake = InitialMinDelegateTake; diff --git a/pallets/admin-utils/src/tests/mod.rs b/pallets/admin-utils/src/tests/mod.rs index 01fb985d9..e1294678b 100644 --- a/pallets/admin-utils/src/tests/mod.rs +++ b/pallets/admin-utils/src/tests/mod.rs @@ -741,6 +741,39 @@ fn test_sudo_set_bonds_moving_average() { }); } +#[test] +fn test_sudo_set_bonds_penalty() { + new_test_ext().execute_with(|| { + let netuid: u16 = 1; + let to_be_set: u16 = 10; + let init_value: u16 = SubtensorModule::get_bonds_penalty(netuid); + add_network(netuid, 10, 0); + assert_eq!( + SubtensorModule::sudo_set_bonds_penalty( + <::RuntimeOrigin>::signed(U256::from(0)), + netuid, + to_be_set + ), + Err(DispatchError::BadOrigin.into()) + ); + assert_eq!( + SubtensorModule::sudo_set_bonds_penalty( + <::RuntimeOrigin>::root(), + netuid + 1, + to_be_set + ), + Err(Error::::NetworkDoesNotExist.into()) + ); + assert_eq!(SubtensorModule::get_bonds_penalty(netuid), init_value); + assert_ok!(SubtensorModule::sudo_set_bonds_penalty( + <::RuntimeOrigin>::root(), + netuid, + to_be_set + )); + assert_eq!(SubtensorModule::get_bonds_penalty(netuid), to_be_set); + }); +} + #[test] fn test_sudo_set_rao_recycled() { new_test_ext().execute_with(|| { diff --git a/pallets/subtensor/src/epoch/math.rs b/pallets/subtensor/src/epoch/math.rs index 57d0f6b6f..9d63ac1c5 100644 --- a/pallets/subtensor/src/epoch/math.rs +++ b/pallets/subtensor/src/epoch/math.rs @@ -1022,6 +1022,79 @@ pub fn weighted_median_col_sparse( median } +// Element-wise interpolation of two matrices: Result = A + ratio * (B - A). +// ratio is has intended range [0, 1] +// ratio=0: Result = A +// ratio=1: Result = B +#[allow(dead_code)] +pub fn interpolate( + mat1: &Vec>, + mat2: &Vec>, + ratio: I32F32, +) -> Vec> { + if ratio == I32F32::from_num(0) { + return mat1.clone(); + } + if ratio == I32F32::from_num(1) { + return mat2.clone(); + } + assert!(mat1.len() == mat2.len()); + if mat1.len() == 0 { + return vec![vec![]; 1]; + } + if mat1[0].len() == 0 { + return vec![vec![]; 1]; + } + let mut result: Vec> = vec![vec![I32F32::from_num(0); mat1[0].len()]; mat1.len()]; + for i in 0..mat1.len() { + assert!(mat1[i].len() == mat2[i].len()); + for j in 0..mat1[i].len() { + result[i][j] = mat1[i][j] + ratio * (mat2[i][j] - mat1[i][j]); + } + } + result +} + +// Element-wise interpolation of two sparse matrices: Result = A + ratio * (B - A). +// ratio is has intended range [0, 1] +// ratio=0: Result = A +// ratio=1: Result = B +#[allow(dead_code)] +pub fn interpolate_sparse( + mat1: &Vec>, + mat2: &Vec>, + columns: u16, + ratio: I32F32, +) -> Vec> { + if ratio == I32F32::from_num(0) { + return mat1.clone(); + } + if ratio == I32F32::from_num(1) { + return mat2.clone(); + } + assert!(mat1.len() == mat2.len()); + let rows = mat1.len(); + let zero: I32F32 = I32F32::from_num(0); + let mut result: Vec> = vec![vec![]; rows]; + for i in 0..rows { + let mut row1: Vec = vec![zero; columns as usize]; + for (j, value) in mat1[i].iter() { + row1[*j as usize] = *value; + } + let mut row2: Vec = vec![zero; columns as usize]; + for (j, value) in mat2[i].iter() { + row2[*j as usize] = *value; + } + for j in 0..columns as usize { + let interp: I32F32 = row1[j] + ratio * (row2[j] - row1[j]); + if zero < interp { + result[i].push((j as u16, interp)) + } + } + } + result +} + // Element-wise product of two matrices. #[allow(dead_code)] pub fn hadamard(mat1: &[Vec], mat2: &[Vec]) -> Vec> { diff --git a/pallets/subtensor/src/epoch/run_epoch.rs b/pallets/subtensor/src/epoch/run_epoch.rs index be06ed59f..e6edd2585 100644 --- a/pallets/subtensor/src/epoch/run_epoch.rs +++ b/pallets/subtensor/src/epoch/run_epoch.rs @@ -137,18 +137,22 @@ impl Pallet { // Compute preranks: r_j = SUM(i) w_ij * s_i let preranks: Vec = matmul(&weights, &active_stake); - // Clip weights at majority consensus - let kappa: I32F32 = Self::get_float_kappa(netuid); // consensus majority ratio, e.g. 51%. + // Consensus majority ratio, e.g. 51%. + let kappa: I32F32 = Self::get_float_kappa(netuid); + // Calculate consensus as stake-weighted median of weights. let consensus: Vec = weighted_median_col(&active_stake, &weights, kappa); - inplace_col_clip(&mut weights, &consensus); - let validator_trust: Vec = row_sum(&weights); + // Clip weights at majority consensus. + let mut clipped_weights: Vec> = weights.clone(); + inplace_col_clip(&mut clipped_weights, &consensus); + // Calculate validator trust as sum of clipped weights set by validator. + let validator_trust: Vec = row_sum(&clipped_weights); // ==================================== // == Ranks, Server Trust, Incentive == // ==================================== // Compute ranks: r_j = SUM(i) w_ij * s_i - let mut ranks: Vec = matmul(&weights, &active_stake); + let mut ranks: Vec = matmul(&clipped_weights, &active_stake); // Compute server trust: ratio of rank after vs. rank before. let trust: Vec = vecdiv(&ranks, &preranks); @@ -161,6 +165,14 @@ impl Pallet { // == Bonds and Dividends == // ========================= + // Get validator bonds penalty in [0, 1]. + let bonds_penalty: I32F32 = Self::get_float_bonds_penalty(netuid); + // Calculate weights for bonds, apply bonds penalty to weights. + // bonds_penalty = 0: weights_for_bonds = weights.clone() + // bonds_penalty = 1: weights_for_bonds = clipped_weights.clone() + let weights_for_bonds: Vec> = + interpolate(&weights, &clipped_weights, bonds_penalty); + // Access network bonds. let mut bonds: Vec> = Self::get_bonds(netuid); inplace_mask_matrix(&outdated, &mut bonds); // mask outdated bonds @@ -168,7 +180,7 @@ impl Pallet { log::trace!("B:\n{:?}\n", &bonds); // Compute bonds delta column normalized. - let mut bonds_delta: Vec> = row_hadamard(&weights, &active_stake); // ΔB = W◦S + let mut bonds_delta: Vec> = row_hadamard(&weights_for_bonds, &active_stake); // ΔB = W◦S inplace_col_normalize(&mut bonds_delta); // sum_i b_ij = 1 log::trace!("ΔB:\n{:?}\n", &bonds_delta); // Compute the Exponential Moving Average (EMA) of bonds. @@ -474,15 +486,18 @@ impl Pallet { let preranks: Vec = matmul_sparse(&weights, &active_stake, n); log::trace!("Ranks (before): {:?}", &preranks); - // Clip weights at majority consensus - let kappa: I32F32 = Self::get_float_kappa(netuid); // consensus majority ratio, e.g. 51%. + // Consensus majority ratio, e.g. 51%. + let kappa: I32F32 = Self::get_float_kappa(netuid); + // Calculate consensus as stake-weighted median of weights. let consensus: Vec = weighted_median_col_sparse(&active_stake, &weights, n, kappa); log::trace!("Consensus: {:?}", &consensus); - weights = col_clip_sparse(&weights, &consensus); - log::trace!("Weights: {:?}", &weights); + // Clip weights at majority consensus. + let clipped_weights: Vec> = col_clip_sparse(&weights, &consensus); + log::trace!("Clipped Weights: {:?}", &clipped_weights); - let validator_trust: Vec = row_sum_sparse(&weights); + // Calculate validator trust as sum of clipped weights set by validator. + let validator_trust: Vec = row_sum_sparse(&clipped_weights); log::trace!("Validator Trust: {:?}", &validator_trust); // ============================= @@ -490,7 +505,7 @@ impl Pallet { // ============================= // Compute ranks: r_j = SUM(i) w_ij * s_i. - let mut ranks: Vec = matmul_sparse(&weights, &active_stake, n); + let mut ranks: Vec = matmul_sparse(&clipped_weights, &active_stake, n); log::trace!("Ranks (after): {:?}", &ranks); // Compute server trust: ratio of rank after vs. rank before. @@ -505,6 +520,14 @@ impl Pallet { // == Bonds and Dividends == // ========================= + // Get validator bonds penalty in [0, 1]. + let bonds_penalty: I32F32 = Self::get_float_bonds_penalty(netuid); + // Calculate weights for bonds, apply bonds penalty to weights. + // bonds_penalty = 0: weights_for_bonds = weights.clone() + // bonds_penalty = 1: weights_for_bonds = clipped_weights.clone() + let weights_for_bonds: Vec> = + interpolate_sparse(&weights, &clipped_weights, n, bonds_penalty); + // Access network bonds. let mut bonds: Vec> = Self::get_bonds_sparse(netuid); log::trace!("B: {:?}", &bonds); @@ -523,7 +546,8 @@ impl Pallet { log::trace!("B (mask+norm): {:?}", &bonds); // Compute bonds delta column normalized. - let mut bonds_delta: Vec> = row_hadamard_sparse(&weights, &active_stake); // ΔB = W◦S (outdated W masked) + let mut bonds_delta: Vec> = + row_hadamard_sparse(&weights_for_bonds, &active_stake); // ΔB = W◦S (outdated W masked) log::trace!("ΔB: {:?}", &bonds_delta); // Normalize bonds delta. @@ -713,6 +737,9 @@ impl Pallet { pub fn get_float_kappa(netuid: u16) -> I32F32 { I32F32::from_num(Self::get_kappa(netuid)).saturating_div(I32F32::from_num(u16::MAX)) } + pub fn get_float_bonds_penalty(netuid: u16) -> I32F32 { + I32F32::from_num(Self::get_bonds_penalty(netuid)).saturating_div(I32F32::from_num(u16::MAX)) + } pub fn get_block_at_registration(netuid: u16) -> Vec { let n = Self::get_subnetwork_n(netuid); diff --git a/pallets/subtensor/src/lib.rs b/pallets/subtensor/src/lib.rs index 7f75568ba..d9421660c 100644 --- a/pallets/subtensor/src/lib.rs +++ b/pallets/subtensor/src/lib.rs @@ -568,6 +568,11 @@ pub mod pallet { pub fn DefaultBondsMovingAverage() -> u64 { T::InitialBondsMovingAverage::get() } + /// Default bonds penalty. + #[pallet::type_value] + pub fn DefaultBondsPenalty() -> u16 { + T::InitialBondsPenalty::get() + } #[pallet::type_value] /// Default validator prune length. pub fn DefaultValidatorPruneLen() -> u64 { @@ -1172,6 +1177,10 @@ pub mod pallet { /// --- MAP ( netuid ) --> bonds_moving_average pub type BondsMovingAverage = StorageMap<_, Identity, u16, u64, ValueQuery, DefaultBondsMovingAverage>; + #[pallet::storage] + /// --- MAP ( netuid ) --> bonds_penalty + pub type BondsPenalty = + StorageMap<_, Identity, u16, u16, ValueQuery, DefaultBondsPenalty>; /// --- MAP ( netuid ) --> weights_set_rate_limit #[pallet::storage] pub type WeightsSetRateLimit = diff --git a/pallets/subtensor/src/macros/config.rs b/pallets/subtensor/src/macros/config.rs index 6595c8a43..49bb44dc1 100644 --- a/pallets/subtensor/src/macros/config.rs +++ b/pallets/subtensor/src/macros/config.rs @@ -93,6 +93,9 @@ mod config { /// Initial bonds moving average. #[pallet::constant] type InitialBondsMovingAverage: Get; + /// Initial bonds penalty. + #[pallet::constant] + type InitialBondsPenalty: Get; /// Initial target registrations per interval. #[pallet::constant] type InitialTargetRegistrationsPerInterval: Get; diff --git a/pallets/subtensor/src/macros/events.rs b/pallets/subtensor/src/macros/events.rs index 30566ac47..cc06c2028 100644 --- a/pallets/subtensor/src/macros/events.rs +++ b/pallets/subtensor/src/macros/events.rs @@ -57,6 +57,8 @@ mod events { ImmunityPeriodSet(u16, u16), /// bonds moving average is set for a subnet. BondsMovingAverageSet(u16, u64), + /// bonds penalty is set for a subnet. + BondsPenaltySet(u16, u16), /// setting the max number of allowed validators on a subnet. MaxAllowedValidatorsSet(u16, u16), /// the axon server information is added to the network. diff --git a/pallets/subtensor/src/tests/epoch.rs b/pallets/subtensor/src/tests/epoch.rs index 43ac6b98c..379936738 100644 --- a/pallets/subtensor/src/tests/epoch.rs +++ b/pallets/subtensor/src/tests/epoch.rs @@ -154,10 +154,14 @@ fn init_run_epochs( random_weights: bool, random_seed: u64, sparse: bool, + bonds_penalty: u16, ) { // === Create the network add_network(netuid, u16::MAX - 1, 0); // set higher tempo to avoid built-in epoch, then manual epoch instead + // === Set bonds penalty + SubtensorModule::set_bonds_penalty(netuid, bonds_penalty); + // === Register uids SubtensorModule::set_max_allowed_uids(netuid, n); for key in 0..n { @@ -403,21 +407,25 @@ fn init_run_epochs( // let epochs: u16 = 1; // let interleave = 2; // log::info!("test_consensus_guarantees ({network_n:?}, {validators_n:?} validators)"); -// for (major_stake, major_weight, minor_weight, weight_stddev) in vec![ -// (0.51, 1., 1., 0.001), -// (0.51, 0.03, 0., 0.001), -// (0.51, 0.51, 0.49, 0.001), -// (0.51, 0.51, 1., 0.001), -// (0.51, 0.61, 0.8, 0.1), -// (0.6, 0.67, 0.65, 0.2), -// (0.6, 0.74, 0.77, 0.4), -// (0.6, 0.76, 0.8, 0.4), -// (0.6, 0.76, 1., 0.4), -// (0.6, 0.92, 1., 0.4), -// (0.6, 0.94, 1., 0.4), -// (0.65, 0.78, 0.85, 0.6), -// (0.7, 0.81, 0.85, 0.8), -// (0.7, 0.83, 0.85, 1.), +// for (major_stake, major_weight, minor_weight, weight_stddev, bonds_penalty) in vec![ +// (0.51, 1., 1., 0.001, u16::MAX), +// (0.51, 0.03, 0., 0.001, u16::MAX), +// (0.51, 0.51, 0.49, 0.001, u16::MAX), +// (0.51, 0.51, 1., 0.001, u16::MAX), +// (0.51, 0.61, 0.8, 0.1, u16::MAX), +// (0.6, 0.67, 0.65, 0.2, u16::MAX), +// (0.6, 0.74, 0.77, 0.4, u16::MAX), +// (0.6, 0.76, 0.8, 0.4, u16::MAX), +// (0.6, 0.73, 1., 0.4, u16::MAX), // bonds_penalty = 100% +// (0.6, 0.74, 1., 0.4, 55800), // bonds_penalty = 85% +// (0.6, 0.76, 1., 0.4, 43690), // bonds_penalty = 66% +// (0.6, 0.78, 1., 0.4, 21845), // bonds_penalty = 33% +// (0.6, 0.79, 1., 0.4, 0), // bonds_penalty = 0% +// (0.6, 0.92, 1., 0.4, u16::MAX), +// (0.6, 0.94, 1., 0.4, u16::MAX), +// (0.65, 0.78, 0.85, 0.6, u16::MAX), +// (0.7, 0.81, 0.85, 0.8, u16::MAX), +// (0.7, 0.83, 0.85, 1., u16::MAX), // ] { // let ( // validators, @@ -455,6 +463,7 @@ fn init_run_epochs( // false, // 0, // false, +// bonds_penalty // ); // let mut major_emission: I64F64 = I64F64::from_num(0); @@ -698,6 +707,7 @@ fn test_512_graph() { false, 0, false, + u16::MAX, ); let bonds = SubtensorModule::get_bonds(netuid); for uid in validators { @@ -744,96 +754,99 @@ fn test_512_graph_random_weights() { let epochs: u16 = 1; log::info!("test_{network_n:?}_graph_random_weights ({validators_n:?} validators)"); for interleave in 0..3 { + // server-self weight off/on for server_self in [false, true] { - // server-self weight off/on - let (validators, servers) = distribute_nodes( - validators_n as usize, - network_n as usize, - interleave as usize, - ); - let server: usize = servers[0] as usize; - let validator: usize = validators[0] as usize; - #[allow(clippy::type_complexity)] - let (mut rank, mut incentive, mut dividend, mut emission, mut bondv, mut bonds): ( - Vec, - Vec, - Vec, - Vec, - Vec, - Vec, - ) = (vec![], vec![], vec![], vec![], vec![], vec![]); - - // Dense epoch - new_test_ext(1).execute_with(|| { - init_run_epochs( - netuid, - network_n, - &validators, - &servers, - epochs, - 1, - server_self, - &[], - false, - &[], - false, - true, - interleave as u64, - false, + for bonds_penalty in vec![0, u16::MAX / 2, u16::MAX] { + let (validators, servers) = distribute_nodes( + validators_n as usize, + network_n as usize, + interleave as usize, ); + let server: usize = servers[0] as usize; + let validator: usize = validators[0] as usize; + let (mut rank, mut incentive, mut dividend, mut emission, mut bondv, mut bonds): ( + Vec, + Vec, + Vec, + Vec, + Vec, + Vec, + ) = (vec![], vec![], vec![], vec![], vec![], vec![]); + + // Dense epoch + new_test_ext(1).execute_with(|| { + init_run_epochs( + netuid, + network_n, + &validators, + &servers, + epochs, + 1, + server_self, + &vec![], + false, + &vec![], + false, + true, + interleave as u64, + false, + bonds_penalty, + ); - let bond = SubtensorModule::get_bonds(netuid); - for uid in 0..network_n { - rank.push(SubtensorModule::get_rank_for_uid(netuid, uid)); - incentive.push(SubtensorModule::get_incentive_for_uid(netuid, uid)); - dividend.push(SubtensorModule::get_dividends_for_uid(netuid, uid)); - emission.push(SubtensorModule::get_emission_for_uid(netuid, uid)); - bondv.push(bond[uid as usize][validator]); - bonds.push(bond[uid as usize][server]); - } - }); + let bond = SubtensorModule::get_bonds(netuid); + for uid in 0..network_n { + rank.push(SubtensorModule::get_rank_for_uid(netuid, uid)); + incentive.push(SubtensorModule::get_incentive_for_uid(netuid, uid)); + dividend.push(SubtensorModule::get_dividends_for_uid(netuid, uid)); + emission.push(SubtensorModule::get_emission_for_uid(netuid, uid)); + bondv.push(bond[uid as usize][validator]); + bonds.push(bond[uid as usize][server]); + } + }); - // Sparse epoch (same random seed as dense) - new_test_ext(1).execute_with(|| { - init_run_epochs( - netuid, - network_n, - &validators, - &servers, - epochs, - 1, - server_self, - &[], - false, - &[], - false, - true, - interleave as u64, - true, - ); - // Assert that dense and sparse epoch results are equal - let bond = SubtensorModule::get_bonds(netuid); - for uid in 0..network_n { - assert_eq!( - SubtensorModule::get_rank_for_uid(netuid, uid), - rank[uid as usize] - ); - assert_eq!( - SubtensorModule::get_incentive_for_uid(netuid, uid), - incentive[uid as usize] - ); - assert_eq!( - SubtensorModule::get_dividends_for_uid(netuid, uid), - dividend[uid as usize] - ); - assert_eq!( - SubtensorModule::get_emission_for_uid(netuid, uid), - emission[uid as usize] + // Sparse epoch (same random seed as dense) + new_test_ext(1).execute_with(|| { + init_run_epochs( + netuid, + network_n, + &validators, + &servers, + epochs, + 1, + server_self, + &vec![], + false, + &vec![], + false, + true, + interleave as u64, + true, + bonds_penalty, ); - assert_eq!(bond[uid as usize][validator], bondv[uid as usize]); - assert_eq!(bond[uid as usize][server], bonds[uid as usize]); - } - }); + // Assert that dense and sparse epoch results are equal + let bond = SubtensorModule::get_bonds(netuid); + for uid in 0..network_n { + assert_eq!( + SubtensorModule::get_rank_for_uid(netuid, uid), + rank[uid as usize] + ); + assert_eq!( + SubtensorModule::get_incentive_for_uid(netuid, uid), + incentive[uid as usize] + ); + assert_eq!( + SubtensorModule::get_dividends_for_uid(netuid, uid), + dividend[uid as usize] + ); + assert_eq!( + SubtensorModule::get_emission_for_uid(netuid, uid), + emission[uid as usize] + ); + assert_eq!(bond[uid as usize][validator], bondv[uid as usize]); + assert_eq!(bond[uid as usize][server], bonds[uid as usize]); + } + }); + } } } } @@ -873,6 +886,7 @@ fn test_512_graph_random_weights() { // false, // 0, // true, +// u16::MAX, // ); // let (total_stake, _, _) = SubtensorModule::get_stake_weights_for_network(netuid); // assert_eq!(total_stake.iter().map(|s| s.to_num::()).sum::(), 21_000_000_000_000_000); @@ -940,6 +954,7 @@ fn test_512_graph_random_weights() { // false, // 0, // true, +// u16::MAX, // ); // let bonds = SubtensorModule::get_bonds(netuid); // for uid in validators { @@ -995,6 +1010,8 @@ fn test_bonds() { SubtensorModule::set_weights_set_rate_limit( netuid, 0 ); SubtensorModule::set_min_allowed_weights( netuid, 1 ); SubtensorModule::set_max_weight_limit( netuid, u16::MAX ); + SubtensorModule::set_bonds_penalty(netuid, u16::MAX); + // === Register [validator1, validator2, validator3, validator4, server1, server2, server3, server4] for key in 0..n as u64 { @@ -1752,6 +1769,7 @@ fn test_outdated_weights() { SubtensorModule::set_target_registrations_per_interval(netuid, n); SubtensorModule::set_min_allowed_weights(netuid, 0); SubtensorModule::set_max_weight_limit(netuid, u16::MAX); + SubtensorModule::set_bonds_penalty(netuid, u16::MAX); assert_eq!(SubtensorModule::get_registrations_this_block(netuid), 0); // === Register [validator1, validator2, server1, server2] @@ -2817,6 +2835,7 @@ fn test_blocks_since_last_step() { // let epochs: u16 = 1; // let interleave = 0; // let weight_stddev: I32F32 = fixed(0.4); +// let bonds_penalty: u16 = u16::MAX; // println!("["); // for _major_stake in vec![0.51, 0.55, 0.6, 0.65, 0.7, 0.75, 0.8, 0.85, 0.9, 0.95, 0.99] { // let major_stake: I32F32 = I32F32::from_num(_major_stake); @@ -2846,7 +2865,7 @@ fn test_blocks_since_last_step() { // ); // // new_test_ext(1).execute_with(|| { -// init_run_epochs(netuid, network_n, &validators, &servers, epochs, 1, true, &stake, true, &weights, true, false, 0, true); +// init_run_epochs(netuid, network_n, &validators, &servers, epochs, 1, true, &stake, true, &weights, true, false, 0, true, bonds_penalty); // // let mut major_emission: I64F64 = I64F64::from_num(0); // let mut minor_emission: I64F64 = I64F64::from_num(0); diff --git a/pallets/subtensor/src/tests/math.rs b/pallets/subtensor/src/tests/math.rs index c5aaca84c..6eb51bc29 100644 --- a/pallets/subtensor/src/tests/math.rs +++ b/pallets/subtensor/src/tests/math.rs @@ -1716,6 +1716,240 @@ fn test_math_weighted_median_col_sparse() { ); } +#[test] +fn test_math_interpolate() { + let mat1: Vec> = vec![vec![]]; + let mat2: Vec> = vec![vec![]]; + let target: Vec> = vec![vec![]]; + let ratio = I32F32::from_num(0); + let result = interpolate(&mat1, &mat2, ratio); + assert_mat_compare(&result, &target, I32F32::from_num(0)); + + let mat1: Vec> = vec![vec![I32F32::from_num(0)]]; + let mat2: Vec> = vec![vec![I32F32::from_num(1)]]; + let target: Vec> = vec![vec![I32F32::from_num(0)]]; + let ratio = I32F32::from_num(0); + let result = interpolate(&mat1, &mat2, ratio); + assert_mat_compare(&result, &target, I32F32::from_num(0)); + + let target: Vec> = vec![vec![I32F32::from_num(1)]]; + let ratio = I32F32::from_num(1); + let result = interpolate(&mat1, &mat2, ratio); + assert_mat_compare(&result, &target, I32F32::from_num(0)); + + let mat1: Vec = vec![0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.]; + let mat2: Vec = vec![0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.]; + let target: Vec = vec![0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.]; + let mat1 = vec_to_mat_fixed(&mat1, 4, false); + let mat2 = vec_to_mat_fixed(&mat2, 4, false); + let ratio = I32F32::from_num(0); + let target = vec_to_mat_fixed(&target, 4, false); + let result = interpolate(&mat1, &mat2, ratio); + assert_mat_compare(&result, &target, I32F32::from_num(0)); + + let ratio = I32F32::from_num(1); + let result = interpolate(&mat1, &mat2, ratio); + assert_mat_compare(&result, &target, I32F32::from_num(0)); + + let mat1: Vec = vec![1., 10., 100., 1000., 10000., 100000.]; + let mat2: Vec = vec![10., 100., 1000., 10000., 100000., 1000000.]; + let target: Vec = vec![1., 10., 100., 1000., 10000., 100000.]; + let mat1 = vec_to_mat_fixed(&mat1, 3, false); + let mat2 = vec_to_mat_fixed(&mat2, 3, false); + let ratio = I32F32::from_num(0); + let target = vec_to_mat_fixed(&target, 3, false); + let result = interpolate(&mat1, &mat2, ratio); + assert_mat_compare(&result, &target, I32F32::from_num(0)); + + let target: Vec = vec![9.1, 91., 910., 9100., 91000., 910000.]; + let ratio = I32F32::from_num(0.9); + let target = vec_to_mat_fixed(&target, 3, false); + let result = interpolate(&mat1, &mat2, ratio); + assert_mat_compare(&result, &target, I32F32::from_num(0.0001)); + + let mat1: Vec = vec![0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.]; + let mat2: Vec = vec![1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1.]; + let target: Vec = vec![0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.]; + let mat1 = vec_to_mat_fixed(&mat1, 4, false); + let mat2 = vec_to_mat_fixed(&mat2, 4, false); + let ratio = I32F32::from_num(0); + let target = vec_to_mat_fixed(&target, 4, false); + let result = interpolate(&mat1, &mat2, ratio); + assert_mat_compare(&result, &target, I32F32::from_num(0)); + + let target: Vec = vec![ + 0.000000001, + 0.000000001, + 0.000000001, + 0.000000001, + 0.000000001, + 0.000000001, + 0.000000001, + 0.000000001, + 0.000000001, + 0.000000001, + 0.000000001, + 0.000000001, + ]; + let ratio = I32F32::from_num(0.000000001); + let target = vec_to_mat_fixed(&target, 4, false); + let result = interpolate(&mat1, &mat2, ratio); + assert_mat_compare(&result, &target, I32F32::from_num(0)); + + let target: Vec = vec![0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5]; + let ratio = I32F32::from_num(0.5); + let target = vec_to_mat_fixed(&target, 4, false); + let result = interpolate(&mat1, &mat2, ratio); + assert_mat_compare(&result, &target, I32F32::from_num(0)); + + let target: Vec = vec![ + 0.9999998808, + 0.9999998808, + 0.9999998808, + 0.9999998808, + 0.9999998808, + 0.9999998808, + 0.9999998808, + 0.9999998808, + 0.9999998808, + 0.9999998808, + 0.9999998808, + 0.9999998808, + ]; + let ratio = I32F32::from_num(0.9999998808); + let target = vec_to_mat_fixed(&target, 4, false); + let result = interpolate(&mat1, &mat2, ratio); + assert_mat_compare(&result, &target, I32F32::from_num(0)); + + let target: Vec = vec![1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1.]; + let ratio = I32F32::from_num(1); + let target = vec_to_mat_fixed(&target, 4, false); + let result = interpolate(&mat1, &mat2, ratio); + assert_mat_compare(&result, &target, I32F32::from_num(0)); +} + +#[test] +fn test_math_interpolate_sparse() { + let mat1: Vec> = vec![vec![]]; + let mat2: Vec> = vec![vec![]]; + let target: Vec> = vec![vec![]]; + let ratio = I32F32::from_num(0); + let result = interpolate_sparse(&mat1, &mat2, 0, ratio); + assert_sparse_mat_compare(&result, &target, I32F32::from_num(0)); + + let mat1: Vec = vec![0.]; + let mat2: Vec = vec![1.]; + let target: Vec = vec![0.]; + let mat1 = vec_to_sparse_mat_fixed(&mat1, 1, false); + let mat2 = vec_to_sparse_mat_fixed(&mat2, 1, false); + let ratio = I32F32::from_num(0); + let target = vec_to_sparse_mat_fixed(&target, 1, false); + let result = interpolate_sparse(&mat1, &mat2, 1, ratio); + assert_sparse_mat_compare(&result, &target, I32F32::from_num(0)); + + let target: Vec = vec![0.5]; + let ratio = I32F32::from_num(0.5); + let target = vec_to_sparse_mat_fixed(&target, 1, false); + let result = interpolate_sparse(&mat1, &mat2, 1, ratio); + assert_sparse_mat_compare(&result, &target, I32F32::from_num(0)); + + let target: Vec = vec![1.]; + let ratio = I32F32::from_num(1); + let target = vec_to_sparse_mat_fixed(&target, 1, false); + let result = interpolate_sparse(&mat1, &mat2, 1, ratio); + assert_sparse_mat_compare(&result, &target, I32F32::from_num(0)); + + let mat1: Vec = vec![0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.]; + let mat2: Vec = vec![0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.]; + let target: Vec = vec![0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.]; + let mat1 = vec_to_sparse_mat_fixed(&mat1, 4, false); + let mat2 = vec_to_sparse_mat_fixed(&mat2, 4, false); + let ratio = I32F32::from_num(0); + let target = vec_to_sparse_mat_fixed(&target, 4, false); + let result = interpolate_sparse(&mat1, &mat2, 3, ratio); + assert_sparse_mat_compare(&result, &target, I32F32::from_num(0)); + + let ratio = I32F32::from_num(1); + let result = interpolate_sparse(&mat1, &mat2, 3, ratio); + assert_sparse_mat_compare(&result, &target, I32F32::from_num(0)); + + let mat1: Vec = vec![1., 0., 100., 1000., 10000., 100000.]; + let mat2: Vec = vec![10., 100., 1000., 10000., 100000., 0.]; + let target: Vec = vec![1., 0., 100., 1000., 10000., 100000.]; + let mat1 = vec_to_sparse_mat_fixed(&mat1, 3, false); + let mat2 = vec_to_sparse_mat_fixed(&mat2, 3, false); + let ratio = I32F32::from_num(0); + let target = vec_to_sparse_mat_fixed(&target, 3, false); + let result = interpolate_sparse(&mat1, &mat2, 2, ratio); + assert_sparse_mat_compare(&result, &target, I32F32::from_num(0)); + + let target: Vec = vec![9.1, 90., 910., 9100., 91000., 10000.]; + let ratio = I32F32::from_num(0.9); + let target = vec_to_sparse_mat_fixed(&target, 3, false); + let result = interpolate_sparse(&mat1, &mat2, 2, ratio); + assert_sparse_mat_compare(&result, &target, I32F32::from_num(0.0001)); + + let mat1: Vec = vec![0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.]; + let mat2: Vec = vec![1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1.]; + let target: Vec = vec![0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.]; + let mat1 = vec_to_sparse_mat_fixed(&mat1, 4, false); + let mat2 = vec_to_sparse_mat_fixed(&mat2, 4, false); + let ratio = I32F32::from_num(0); + let target = vec_to_sparse_mat_fixed(&target, 4, false); + let result = interpolate_sparse(&mat1, &mat2, 3, ratio); + assert_sparse_mat_compare(&result, &target, I32F32::from_num(0)); + + let target: Vec = vec![ + 0.000000001, + 0.000000001, + 0.000000001, + 0.000000001, + 0.000000001, + 0.000000001, + 0.000000001, + 0.000000001, + 0.000000001, + 0.000000001, + 0.000000001, + 0.000000001, + ]; + let ratio = I32F32::from_num(0.000000001); + let target = vec_to_sparse_mat_fixed(&target, 4, false); + let result = interpolate_sparse(&mat1, &mat2, 3, ratio); + assert_sparse_mat_compare(&result, &target, I32F32::from_num(0)); + + let target: Vec = vec![0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5]; + let ratio = I32F32::from_num(0.5); + let target = vec_to_sparse_mat_fixed(&target, 4, false); + let result = interpolate_sparse(&mat1, &mat2, 3, ratio); + assert_sparse_mat_compare(&result, &target, I32F32::from_num(0)); + + let target: Vec = vec![ + 0.9999998808, + 0.9999998808, + 0.9999998808, + 0.9999998808, + 0.9999998808, + 0.9999998808, + 0.9999998808, + 0.9999998808, + 0.9999998808, + 0.9999998808, + 0.9999998808, + 0.9999998808, + ]; + let ratio = I32F32::from_num(0.9999998808); + let target = vec_to_sparse_mat_fixed(&target, 4, false); + let result = interpolate_sparse(&mat1, &mat2, 3, ratio); + assert_sparse_mat_compare(&result, &target, I32F32::from_num(0)); + + let target: Vec = vec![1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1.]; + let ratio = I32F32::from_num(1); + let target = vec_to_sparse_mat_fixed(&target, 4, false); + let result = interpolate_sparse(&mat1, &mat2, 3, ratio); + assert_sparse_mat_compare(&result, &target, I32F32::from_num(0)); +} + #[test] fn test_math_hadamard() { let mat2: Vec = vec![1., 2., 3., 4., 5., 6., 7., 8., 9., 10., 11., 12.]; diff --git a/pallets/subtensor/src/tests/mock.rs b/pallets/subtensor/src/tests/mock.rs index a6f9caed7..facab56ca 100644 --- a/pallets/subtensor/src/tests/mock.rs +++ b/pallets/subtensor/src/tests/mock.rs @@ -138,6 +138,7 @@ parameter_types! { pub const InitialImmunityPeriod: u16 = 2; pub const InitialMaxAllowedUids: u16 = 2; pub const InitialBondsMovingAverage: u64 = 900_000; + pub const InitialBondsPenalty:u16 = 0; pub const InitialStakePruningMin: u16 = 0; pub const InitialFoundationDistribution: u64 = 0; pub const InitialDefaultDelegateTake: u16 = 11_796; // 18%, same as in production @@ -373,6 +374,7 @@ impl crate::Config for Test { type InitialMaxRegistrationsPerBlock = InitialMaxRegistrationsPerBlock; type InitialPruningScore = InitialPruningScore; type InitialBondsMovingAverage = InitialBondsMovingAverage; + type InitialBondsPenalty = InitialBondsPenalty; type InitialMaxAllowedValidators = InitialMaxAllowedValidators; type InitialDefaultDelegateTake = InitialDefaultDelegateTake; type InitialMinDelegateTake = InitialMinDelegateTake; diff --git a/pallets/subtensor/src/utils/misc.rs b/pallets/subtensor/src/utils/misc.rs index 25f547c5d..f1e0c7eb4 100644 --- a/pallets/subtensor/src/utils/misc.rs +++ b/pallets/subtensor/src/utils/misc.rs @@ -566,6 +566,14 @@ impl Pallet { Self::deposit_event(Event::BondsMovingAverageSet(netuid, bonds_moving_average)); } + pub fn get_bonds_penalty(netuid: u16) -> u16 { + BondsPenalty::::get(netuid) + } + pub fn set_bonds_penalty(netuid: u16, bonds_penalty: u16) { + BondsPenalty::::insert(netuid, bonds_penalty); + Self::deposit_event(Event::BondsPenaltySet(netuid, bonds_penalty)); + } + pub fn get_max_registrations_per_block(netuid: u16) -> u16 { MaxRegistrationsPerBlock::::get(netuid) } diff --git a/runtime/src/lib.rs b/runtime/src/lib.rs index 72221b9ff..2d138c039 100644 --- a/runtime/src/lib.rs +++ b/runtime/src/lib.rs @@ -1011,6 +1011,7 @@ parameter_types! { pub const SubtensorInitialMaxRegistrationsPerBlock: u16 = 1; pub const SubtensorInitialPruningScore : u16 = u16::MAX; pub const SubtensorInitialBondsMovingAverage: u64 = 900_000; + pub const SubtensorInitialBondsPenalty: u16 = 0; pub const SubtensorInitialDefaultTake: u16 = 11_796; // 18% honest number. pub const SubtensorInitialMinDelegateTake: u16 = 0; // Allow 0% delegate take pub const SubtensorInitialDefaultChildKeyTake: u16 = 0; // Allow 0% childkey take @@ -1058,6 +1059,7 @@ impl pallet_subtensor::Config for Runtime { type InitialKappa = SubtensorInitialKappa; type InitialMaxAllowedUids = SubtensorInitialMaxAllowedUids; type InitialBondsMovingAverage = SubtensorInitialBondsMovingAverage; + type InitialBondsPenalty = SubtensorInitialBondsPenalty; type InitialIssuance = SubtensorInitialIssuance; type InitialMinAllowedWeights = SubtensorInitialMinAllowedWeights; type InitialEmissionValue = SubtensorInitialEmissionValue; From 4d91ec4819cbffe401dd8b73023012f954a972ef Mon Sep 17 00:00:00 2001 From: Andreea Popescu Date: Wed, 15 Jan 2025 12:54:20 +0000 Subject: [PATCH 19/20] fix safe math --- pallets/subtensor/src/epoch/math.rs | 59 +++++++++++++++++------------ 1 file changed, 35 insertions(+), 24 deletions(-) diff --git a/pallets/subtensor/src/epoch/math.rs b/pallets/subtensor/src/epoch/math.rs index 9d63ac1c5..3f7c5d89d 100644 --- a/pallets/subtensor/src/epoch/math.rs +++ b/pallets/subtensor/src/epoch/math.rs @@ -1027,29 +1027,28 @@ pub fn weighted_median_col_sparse( // ratio=0: Result = A // ratio=1: Result = B #[allow(dead_code)] -pub fn interpolate( - mat1: &Vec>, - mat2: &Vec>, - ratio: I32F32, -) -> Vec> { +pub fn interpolate(mat1: &[Vec], mat2: &[Vec], ratio: I32F32) -> Vec> { if ratio == I32F32::from_num(0) { - return mat1.clone(); + return mat1.to_owned(); } if ratio == I32F32::from_num(1) { - return mat2.clone(); + return mat2.to_owned(); } assert!(mat1.len() == mat2.len()); - if mat1.len() == 0 { + if mat1.is_empty() { return vec![vec![]; 1]; } - if mat1[0].len() == 0 { + if mat1.first().unwrap_or(&vec![]).is_empty() { return vec![vec![]; 1]; } - let mut result: Vec> = vec![vec![I32F32::from_num(0); mat1[0].len()]; mat1.len()]; - for i in 0..mat1.len() { - assert!(mat1[i].len() == mat2[i].len()); - for j in 0..mat1[i].len() { - result[i][j] = mat1[i][j] + ratio * (mat2[i][j] - mat1[i][j]); + let mut result: Vec> = + vec![vec![I32F32::from_num(0); mat1.first().unwrap_or(&vec![]).len()]; mat1.len()]; + for (i, (row1, row2)) in mat1.iter().zip(mat2.iter()).enumerate() { + assert!(row1.len() == row2.len()); + for (j, (&v1, &v2)) in row1.iter().zip(row2.iter()).enumerate() { + if let Some(res) = result.get_mut(i).unwrap_or(&mut vec![]).get_mut(j) { + *res = v1.saturating_add(ratio.saturating_mul(v2.saturating_sub(v1))); + } } } result @@ -1061,16 +1060,16 @@ pub fn interpolate( // ratio=1: Result = B #[allow(dead_code)] pub fn interpolate_sparse( - mat1: &Vec>, - mat2: &Vec>, + mat1: &[Vec<(u16, I32F32)>], + mat2: &[Vec<(u16, I32F32)>], columns: u16, ratio: I32F32, ) -> Vec> { if ratio == I32F32::from_num(0) { - return mat1.clone(); + return mat1.to_owned(); } if ratio == I32F32::from_num(1) { - return mat2.clone(); + return mat2.to_owned(); } assert!(mat1.len() == mat2.len()); let rows = mat1.len(); @@ -1078,17 +1077,29 @@ pub fn interpolate_sparse( let mut result: Vec> = vec![vec![]; rows]; for i in 0..rows { let mut row1: Vec = vec![zero; columns as usize]; - for (j, value) in mat1[i].iter() { - row1[*j as usize] = *value; + if let Some(row) = mat1.get(i) { + for (j, value) in row { + if let Some(entry) = row1.get_mut(*j as usize) { + *entry = *value; + } + } } let mut row2: Vec = vec![zero; columns as usize]; - for (j, value) in mat2[i].iter() { - row2[*j as usize] = *value; + if let Some(row) = mat2.get(i) { + for (j, value) in row { + if let Some(entry) = row2.get_mut(*j as usize) { + *entry = *value; + } + } } for j in 0..columns as usize { - let interp: I32F32 = row1[j] + ratio * (row2[j] - row1[j]); + let v1 = row1.get(j).unwrap_or(&zero); + let v2 = row2.get(j).unwrap_or(&zero); + let interp = v1.saturating_add(ratio.saturating_mul(v2.saturating_sub(*v1))); if zero < interp { - result[i].push((j as u16, interp)) + if let Some(res) = result.get_mut(i) { + res.push((j as u16, interp)); + } } } } From 43a13cf5cbb11b0f52896ecc2752bc9334171c66 Mon Sep 17 00:00:00 2001 From: Andreea Popescu Date: Wed, 15 Jan 2025 14:19:24 +0000 Subject: [PATCH 20/20] add bonds_penalty admin-utils --- hyperparameters.md | 8 +++-- pallets/admin-utils/src/benchmarking.rs | 8 +++++ pallets/admin-utils/src/lib.rs | 25 +++++++++++++ pallets/admin-utils/src/tests/mod.rs | 14 ++++---- pallets/admin-utils/src/weights.rs | 27 ++++++++++++++ pallets/subtensor/src/epoch/math.rs | 4 +-- pallets/subtensor/src/tests/epoch.rs | 10 +++--- pallets/subtensor/src/tests/math.rs | 48 ++++++++++++------------- 8 files changed, 103 insertions(+), 41 deletions(-) diff --git a/hyperparameters.md b/hyperparameters.md index 96f955437..c8d2ce110 100644 --- a/hyperparameters.md +++ b/hyperparameters.md @@ -7,7 +7,7 @@ TxRateLimit: u64 = 1; // [1 @ 64,888] ### netuid 1 (text_prompting) ```rust Rho: u16 = 10; -Kappa: u16 = 32_767; // 0.5 = 65535/2 +Kappa: u16 = 32_767; // 0.5 = 65535/2 MaxAllowedUids: u16 = 1024; Issuance: u64 = 0; MinAllowedWeights: u16 = 8; @@ -32,10 +32,11 @@ ActivityCutoff: u16 = 5000; MaxRegistrationsPerBlock: u16 = 1; PruningScore : u16 = u16::MAX; BondsMovingAverage: u64 = 900_000; +BondsPenalty: u16 = 0; WeightsVersionKey: u64 = 1020; MinDifficulty: u64 = 10_000_000; MaxDifficulty: u64 = u64::MAX / 4; -ServingRateLimit: u64 = 10; +ServingRateLimit: u64 = 10; Burn: u64 = 1_000_000_000; // 1 tao MinBurn: u64 = 1_000_000_000; // 1 tao MaxBurn: u64 = 100_000_000_000; // 100 tao @@ -45,7 +46,7 @@ WeightsSetRateLimit: u64 = 100; ### netuid 3 (causallmnext) ```rust Rho: u16 = 10; -Kappa: u16 = 32_767; // 0.5 = 65535/2 +Kappa: u16 = 32_767; // 0.5 = 65535/2 MaxAllowedUids: u16 = 4096; Issuance: u64 = 0; MinAllowedWeights: u16 = 50; @@ -70,6 +71,7 @@ ActivityCutoff: u16 = 5000; // [5000 @ 7,163] MaxRegistrationsPerBlock: u16 = 1; PruningScore : u16 = u16::MAX; BondsMovingAverage: u64 = 900_000; +BondsPenalty: u16 = 0; WeightsVersionKey: u64 = 400; MinDifficulty: u64 = 10_000_000; MaxDifficulty: u64 = u64::MAX / 4; diff --git a/pallets/admin-utils/src/benchmarking.rs b/pallets/admin-utils/src/benchmarking.rs index abbcd0f16..af9b68051 100644 --- a/pallets/admin-utils/src/benchmarking.rs +++ b/pallets/admin-utils/src/benchmarking.rs @@ -100,6 +100,14 @@ mod benchmarks { _(RawOrigin::Root, 1u16/*netuid*/, 100u64/*bonds_moving_average*/)/*sudo_set_bonds_moving_average*/; } + #[benchmark] + fn sudo_set_bonds_penalty() { + pallet_subtensor::Pallet::::init_new_network(1u16 /*netuid*/, 1u16 /*tempo*/); + + #[extrinsic_call] + _(RawOrigin::Root, 1u16/*netuid*/, 100u16/*bonds_penalty*/)/*sudo_set_bonds_penalty*/; + } + #[benchmark] fn sudo_set_max_allowed_validators() { pallet_subtensor::Pallet::::init_new_network(1u16 /*netuid*/, 1u16 /*tempo*/); diff --git a/pallets/admin-utils/src/lib.rs b/pallets/admin-utils/src/lib.rs index f6b132148..7de39aa38 100644 --- a/pallets/admin-utils/src/lib.rs +++ b/pallets/admin-utils/src/lib.rs @@ -685,6 +685,31 @@ pub mod pallet { Ok(()) } + /// The extrinsic sets the bonds penalty for a subnet. + /// It is only callable by the root account or subnet owner. + /// The extrinsic will call the Subtensor pallet to set the bonds penalty. + #[pallet::call_index(60)] + #[pallet::weight(::WeightInfo::sudo_set_bonds_penalty())] + pub fn sudo_set_bonds_penalty( + origin: OriginFor, + netuid: u16, + bonds_penalty: u16, + ) -> DispatchResult { + pallet_subtensor::Pallet::::ensure_subnet_owner_or_root(origin, netuid)?; + + ensure!( + pallet_subtensor::Pallet::::if_subnet_exist(netuid), + Error::::SubnetDoesNotExist + ); + pallet_subtensor::Pallet::::set_bonds_penalty(netuid, bonds_penalty); + log::debug!( + "BondsPenalty( netuid: {:?} bonds_penalty: {:?} ) ", + netuid, + bonds_penalty + ); + Ok(()) + } + /// The extrinsic sets the maximum registrations per block for a subnet. /// It is only callable by the root account. /// The extrinsic will call the Subtensor pallet to set the maximum registrations per block. diff --git a/pallets/admin-utils/src/tests/mod.rs b/pallets/admin-utils/src/tests/mod.rs index e1294678b..a3b771444 100644 --- a/pallets/admin-utils/src/tests/mod.rs +++ b/pallets/admin-utils/src/tests/mod.rs @@ -746,26 +746,26 @@ fn test_sudo_set_bonds_penalty() { new_test_ext().execute_with(|| { let netuid: u16 = 1; let to_be_set: u16 = 10; + add_network(netuid, 10); let init_value: u16 = SubtensorModule::get_bonds_penalty(netuid); - add_network(netuid, 10, 0); assert_eq!( - SubtensorModule::sudo_set_bonds_penalty( - <::RuntimeOrigin>::signed(U256::from(0)), + AdminUtils::sudo_set_bonds_penalty( + <::RuntimeOrigin>::signed(U256::from(1)), netuid, to_be_set ), - Err(DispatchError::BadOrigin.into()) + Err(DispatchError::BadOrigin) ); assert_eq!( - SubtensorModule::sudo_set_bonds_penalty( + AdminUtils::sudo_set_bonds_penalty( <::RuntimeOrigin>::root(), netuid + 1, to_be_set ), - Err(Error::::NetworkDoesNotExist.into()) + Err(Error::::SubnetDoesNotExist.into()) ); assert_eq!(SubtensorModule::get_bonds_penalty(netuid), init_value); - assert_ok!(SubtensorModule::sudo_set_bonds_penalty( + assert_ok!(AdminUtils::sudo_set_bonds_penalty( <::RuntimeOrigin>::root(), netuid, to_be_set diff --git a/pallets/admin-utils/src/weights.rs b/pallets/admin-utils/src/weights.rs index cb7017023..6ef952354 100644 --- a/pallets/admin-utils/src/weights.rs +++ b/pallets/admin-utils/src/weights.rs @@ -42,6 +42,7 @@ pub trait WeightInfo { fn sudo_set_weights_set_rate_limit() -> Weight; fn sudo_set_weights_version_key() -> Weight; fn sudo_set_bonds_moving_average() -> Weight; + fn sudo_set_bonds_penalty() -> Weight; fn sudo_set_max_allowed_validators() -> Weight; fn sudo_set_difficulty() -> Weight; fn sudo_set_adjustment_interval() -> Weight; @@ -182,6 +183,19 @@ impl WeightInfo for SubstrateWeight { } /// Storage: SubtensorModule NetworksAdded (r:1 w:0) /// Proof Skipped: SubtensorModule NetworksAdded (max_values: None, max_size: None, mode: Measured) + /// Storage: SubtensorModule BondsPenalty (r:0 w:1) + /// Proof Skipped: SubtensorModule BondsPenalty (max_values: None, max_size: None, mode: Measured) + fn sudo_set_bonds_penalty() -> Weight { + // Proof Size summary in bytes: + // Measured: `1111` + // Estimated: `4697` + // Minimum execution time: 46_099_000 picoseconds. + Weight::from_parts(47_510_000, 4697) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: SubtensorModule NetworksAdded (r:1 w:0) + /// Proof Skipped: SubtensorModule NetworksAdded (max_values: None, max_size: None, mode: Measured) /// Storage: SubtensorModule MaxAllowedUids (r:1 w:0) /// Proof Skipped: SubtensorModule MaxAllowedUids (max_values: None, max_size: None, mode: Measured) /// Storage: SubtensorModule MaxAllowedValidators (r:0 w:1) @@ -559,6 +573,19 @@ impl WeightInfo for () { } /// Storage: SubtensorModule NetworksAdded (r:1 w:0) /// Proof Skipped: SubtensorModule NetworksAdded (max_values: None, max_size: None, mode: Measured) + /// Storage: SubtensorModule BondsPenalty (r:0 w:1) + /// Proof Skipped: SubtensorModule BondsPenalty (max_values: None, max_size: None, mode: Measured) + fn sudo_set_bonds_penalty() -> Weight { + // Proof Size summary in bytes: + // Measured: `1111` + // Estimated: `4697` + // Minimum execution time: 46_099_000 picoseconds. + Weight::from_parts(47_510_000, 4697) + .saturating_add(RocksDbWeight::get().reads(1_u64)) + .saturating_add(RocksDbWeight::get().writes(1_u64)) + } + /// Storage: SubtensorModule NetworksAdded (r:1 w:0) + /// Proof Skipped: SubtensorModule NetworksAdded (max_values: None, max_size: None, mode: Measured) /// Storage: SubtensorModule MaxAllowedUids (r:1 w:0) /// Proof Skipped: SubtensorModule MaxAllowedUids (max_values: None, max_size: None, mode: Measured) /// Storage: SubtensorModule MaxAllowedValidators (r:0 w:1) diff --git a/pallets/subtensor/src/epoch/math.rs b/pallets/subtensor/src/epoch/math.rs index 3f7c5d89d..616a9b78b 100644 --- a/pallets/subtensor/src/epoch/math.rs +++ b/pallets/subtensor/src/epoch/math.rs @@ -1023,7 +1023,7 @@ pub fn weighted_median_col_sparse( } // Element-wise interpolation of two matrices: Result = A + ratio * (B - A). -// ratio is has intended range [0, 1] +// ratio has intended range [0, 1] // ratio=0: Result = A // ratio=1: Result = B #[allow(dead_code)] @@ -1055,7 +1055,7 @@ pub fn interpolate(mat1: &[Vec], mat2: &[Vec], ratio: I32F32) -> } // Element-wise interpolation of two sparse matrices: Result = A + ratio * (B - A). -// ratio is has intended range [0, 1] +// ratio has intended range [0, 1] // ratio=0: Result = A // ratio=1: Result = B #[allow(dead_code)] diff --git a/pallets/subtensor/src/tests/epoch.rs b/pallets/subtensor/src/tests/epoch.rs index 379936738..7973a44e1 100644 --- a/pallets/subtensor/src/tests/epoch.rs +++ b/pallets/subtensor/src/tests/epoch.rs @@ -756,7 +756,7 @@ fn test_512_graph_random_weights() { for interleave in 0..3 { // server-self weight off/on for server_self in [false, true] { - for bonds_penalty in vec![0, u16::MAX / 2, u16::MAX] { + for bonds_penalty in [0, u16::MAX / 2, u16::MAX] { let (validators, servers) = distribute_nodes( validators_n as usize, network_n as usize, @@ -783,9 +783,9 @@ fn test_512_graph_random_weights() { epochs, 1, server_self, - &vec![], + &[], false, - &vec![], + &[], false, true, interleave as u64, @@ -814,9 +814,9 @@ fn test_512_graph_random_weights() { epochs, 1, server_self, - &vec![], + &[], false, - &vec![], + &[], false, true, interleave as u64, diff --git a/pallets/subtensor/src/tests/math.rs b/pallets/subtensor/src/tests/math.rs index 6eb51bc29..84fcc4aac 100644 --- a/pallets/subtensor/src/tests/math.rs +++ b/pallets/subtensor/src/tests/math.rs @@ -1803,18 +1803,18 @@ fn test_math_interpolate() { assert_mat_compare(&result, &target, I32F32::from_num(0)); let target: Vec = vec![ - 0.9999998808, - 0.9999998808, - 0.9999998808, - 0.9999998808, - 0.9999998808, - 0.9999998808, - 0.9999998808, - 0.9999998808, - 0.9999998808, - 0.9999998808, - 0.9999998808, - 0.9999998808, + 0.999_999_9, + 0.999_999_9, + 0.999_999_9, + 0.999_999_9, + 0.999_999_9, + 0.999_999_9, + 0.999_999_9, + 0.999_999_9, + 0.999_999_9, + 0.999_999_9, + 0.999_999_9, + 0.999_999_9, ]; let ratio = I32F32::from_num(0.9999998808); let target = vec_to_mat_fixed(&target, 4, false); @@ -1925,18 +1925,18 @@ fn test_math_interpolate_sparse() { assert_sparse_mat_compare(&result, &target, I32F32::from_num(0)); let target: Vec = vec![ - 0.9999998808, - 0.9999998808, - 0.9999998808, - 0.9999998808, - 0.9999998808, - 0.9999998808, - 0.9999998808, - 0.9999998808, - 0.9999998808, - 0.9999998808, - 0.9999998808, - 0.9999998808, + 0.999_999_9, + 0.999_999_9, + 0.999_999_9, + 0.999_999_9, + 0.999_999_9, + 0.999_999_9, + 0.999_999_9, + 0.999_999_9, + 0.999_999_9, + 0.999_999_9, + 0.999_999_9, + 0.999_999_9, ]; let ratio = I32F32::from_num(0.9999998808); let target = vec_to_sparse_mat_fixed(&target, 4, false);