From bc8c514aa39b307c7e8a944152d189641cba16ff Mon Sep 17 00:00:00 2001 From: open-junius <zhou@opentensor.dev> Date: Tue, 10 Dec 2024 21:15:23 +0800 Subject: [PATCH 001/145] add subnet precompile contract --- runtime/src/precompiles/mod.rs | 98 +++++++++++- runtime/src/precompiles/solidity/subnet.abi | 32 ++++ runtime/src/precompiles/solidity/subnet.sol | 10 ++ runtime/src/precompiles/staking.rs | 160 ++++++++++---------- runtime/src/precompiles/subnet.rs | 130 ++++++++++++++++ 5 files changed, 345 insertions(+), 85 deletions(-) create mode 100644 runtime/src/precompiles/solidity/subnet.abi create mode 100644 runtime/src/precompiles/solidity/subnet.sol create mode 100644 runtime/src/precompiles/subnet.rs diff --git a/runtime/src/precompiles/mod.rs b/runtime/src/precompiles/mod.rs index 22f2a4881..ff8efa599 100644 --- a/runtime/src/precompiles/mod.rs +++ b/runtime/src/precompiles/mod.rs @@ -3,21 +3,35 @@ use sp_core::{hashing::keccak_256, H160}; use sp_runtime::AccountId32; use pallet_evm::{ - ExitError, IsPrecompileResult, Precompile, PrecompileFailure, PrecompileHandle, + AddressMapping, BalanceConverter, ExitError, ExitSucceed, HashedAddressMapping, + IsPrecompileResult, Precompile, PrecompileFailure, PrecompileHandle, PrecompileOutput, PrecompileResult, PrecompileSet, }; use pallet_evm_precompile_modexp::Modexp; use pallet_evm_precompile_sha3fips::Sha3FIPS256; use pallet_evm_precompile_simple::{ECRecover, ECRecoverPublicKey, Identity, Ripemd160, Sha256}; +use frame_system::RawOrigin; + +use sp_core::crypto::Ss58Codec; +use sp_core::U256; +use sp_runtime::traits::Dispatchable; +use sp_runtime::traits::{BlakeTwo256, UniqueSaturatedInto}; + +use sp_std::vec; + +use crate::{Runtime, RuntimeCall}; + // Include custom precompiles mod balance_transfer; mod ed25519; mod staking; +mod subnet; use balance_transfer::*; use ed25519::*; use staking::*; +use subnet::*; pub struct FrontierPrecompiles<R>(PhantomData<R>); @@ -37,7 +51,7 @@ where pub fn new() -> Self { Self(Default::default()) } - pub fn used_addresses() -> [H160; 10] { + pub fn used_addresses() -> [H160; 11] { [ hash(1), hash(2), @@ -49,6 +63,7 @@ where hash(EDVERIFY_PRECOMPILE_INDEX), hash(BALANCE_TRANSFER_INDEX), hash(STAKING_PRECOMPILE_INDEX), + hash(SUBNET_PRECOMPILE_INDEX), ] } } @@ -73,6 +88,7 @@ where Some(BalanceTransferPrecompile::execute(handle)) } a if a == hash(STAKING_PRECOMPILE_INDEX) => Some(StakingPrecompile::execute(handle)), + a if a == hash(SUBNET_PRECOMPILE_INDEX) => Some(SubnetPrecompile::execute(handle)), _ => None, } } @@ -118,8 +134,86 @@ pub fn get_slice(data: &[u8], from: usize, to: usize) -> Result<&[u8], Precompil if let Some(slice) = maybe_slice { Ok(slice) } else { + log::error!( + "fail to get slice from data, {:?}, from {}, to {}", + &data, + from, + to + ); Err(PrecompileFailure::Error { exit_status: ExitError::InvalidRange, }) } } + +fn transfer_back_to_caller( + account_id: &AccountId32, + amount: U256, +) -> Result<(), PrecompileFailure> { + // this is staking smart contract's(0x0000000000000000000000000000000000000801) sr25519 address + let smart_contract_account_id = + match AccountId32::from_ss58check("5CwnBK9Ack1mhznmCnwiibCNQc174pYQVktYW3ayRpLm4K2X") { + Ok(addr) => addr, + Err(_) => { + return Err(PrecompileFailure::Error { + exit_status: ExitError::Other("Invalid SS58 address".into()), + }); + } + }; + let amount_sub = + <Runtime as pallet_evm::Config>::BalanceConverter::into_substrate_balance(amount) + .ok_or(ExitError::OutOfFund)?; + + // Create a transfer call from the smart contract to the caller + let transfer_call = + RuntimeCall::Balances(pallet_balances::Call::<Runtime>::transfer_allow_death { + dest: account_id.clone().into(), + value: amount_sub.unique_saturated_into(), + }); + + // Execute the transfer + let transfer_result = + transfer_call.dispatch(RawOrigin::Signed(smart_contract_account_id).into()); + + if let Err(dispatch_error) = transfer_result { + log::error!( + "Transfer back to caller failed. Error: {:?}", + dispatch_error + ); + return Err(PrecompileFailure::Error { + exit_status: ExitError::Other("Transfer back to caller failed".into()), + }); + } + + Ok(()) +} + +fn dispatch(handle: &mut impl PrecompileHandle, call: RuntimeCall) -> PrecompileResult { + let account_id = + <HashedAddressMapping<BlakeTwo256> as AddressMapping<AccountId32>>::into_account_id( + handle.context().caller, + ); + + // Transfer the amount back to the caller before executing the staking operation + // let caller = handle.context().caller; + let amount = handle.context().apparent_value; + + if !amount.is_zero() { + transfer_back_to_caller(&account_id, amount)?; + } + + let result = call.dispatch(RawOrigin::Signed(account_id.clone()).into()); + match &result { + Ok(post_info) => log::info!("Dispatch succeeded. Post info: {:?}", post_info), + Err(dispatch_error) => log::error!("Dispatch failed. Error: {:?}", dispatch_error), + } + match result { + Ok(_) => Ok(PrecompileOutput { + exit_status: ExitSucceed::Returned, + output: vec![], + }), + Err(_) => Err(PrecompileFailure::Error { + exit_status: ExitError::Other("Subtensor call failed".into()), + }), + } +} diff --git a/runtime/src/precompiles/solidity/subnet.abi b/runtime/src/precompiles/solidity/subnet.abi new file mode 100644 index 000000000..a89cf91f1 --- /dev/null +++ b/runtime/src/precompiles/solidity/subnet.abi @@ -0,0 +1,32 @@ +[ + { + "inputs": [ + { + "internalType": "bytes", + "name": "subnetName", + "type": "bytes" + }, + { + "internalType": "bytes", + "name": "githubRepo", + "type": "bytes" + }, + { + "internalType": "bytes", + "name": "subnetContact", + "type": "bytes" + } + ], + "name": "registerNetwork", + "outputs": [], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [], + "name": "registerNetwork", + "outputs": [], + "stateMutability": "payable", + "type": "function" + } +] \ No newline at end of file diff --git a/runtime/src/precompiles/solidity/subnet.sol b/runtime/src/precompiles/solidity/subnet.sol new file mode 100644 index 000000000..e2857ad63 --- /dev/null +++ b/runtime/src/precompiles/solidity/subnet.sol @@ -0,0 +1,10 @@ +pragma solidity ^0.8.0; + +address constant ISTAKING_ADDRESS = 0x0000000000000000000000000000000000000803; + +interface ISubnet { + /// Registers a new network without specifying details. + function registerNetwork() external payable; + /// Registers a new network with specified subnet name, GitHub repository, and contact information. + function registerNetwork(bytes subnetName, bytes githubRepo, bytes subnetContact) external payable; +} \ No newline at end of file diff --git a/runtime/src/precompiles/staking.rs b/runtime/src/precompiles/staking.rs index e6237dfcf..fc1e7b512 100644 --- a/runtime/src/precompiles/staking.rs +++ b/runtime/src/precompiles/staking.rs @@ -25,18 +25,12 @@ // - Precompile checks the result of do_remove_stake and, in case of a failure, reverts the transaction. // -use frame_system::RawOrigin; -use pallet_evm::{AddressMapping, BalanceConverter, HashedAddressMapping}; -use pallet_evm::{ - ExitError, ExitSucceed, PrecompileFailure, PrecompileHandle, PrecompileOutput, PrecompileResult, -}; -use sp_core::crypto::Ss58Codec; +use pallet_evm::BalanceConverter; +use pallet_evm::{ExitError, PrecompileFailure, PrecompileHandle, PrecompileResult}; use sp_core::U256; -use sp_runtime::traits::Dispatchable; -use sp_runtime::traits::{BlakeTwo256, UniqueSaturatedInto}; -use sp_runtime::AccountId32; +use sp_runtime::traits::UniqueSaturatedInto; -use crate::precompiles::{get_method_id, get_slice}; +use crate::precompiles::{dispatch, get_method_id, get_slice}; use sp_std::vec; use crate::{Runtime, RuntimeCall}; @@ -78,7 +72,7 @@ impl StakingPrecompile { amount_staked: amount_sub.unique_saturated_into(), }); // Dispatch the add_stake call - Self::dispatch(handle, call) + dispatch(handle, call) } fn remove_stake(handle: &mut impl PrecompileHandle, data: &[u8]) -> PrecompileResult { let hotkey = Self::parse_hotkey(data)?.into(); @@ -98,7 +92,7 @@ impl StakingPrecompile { hotkey, amount_unstaked: amount_sub.unique_saturated_into(), }); - Self::dispatch(handle, call) + dispatch(handle, call) } fn parse_hotkey(data: &[u8]) -> Result<[u8; 32], PrecompileFailure> { @@ -112,75 +106,75 @@ impl StakingPrecompile { Ok(hotkey) } - fn dispatch(handle: &mut impl PrecompileHandle, call: RuntimeCall) -> PrecompileResult { - let account_id = - <HashedAddressMapping<BlakeTwo256> as AddressMapping<AccountId32>>::into_account_id( - handle.context().caller, - ); - - // Transfer the amount back to the caller before executing the staking operation - // let caller = handle.context().caller; - let amount = handle.context().apparent_value; - - if !amount.is_zero() { - Self::transfer_back_to_caller(&account_id, amount)?; - } - - let result = call.dispatch(RawOrigin::Signed(account_id.clone()).into()); - match &result { - Ok(post_info) => log::info!("Dispatch succeeded. Post info: {:?}", post_info), - Err(dispatch_error) => log::error!("Dispatch failed. Error: {:?}", dispatch_error), - } - match result { - Ok(_) => Ok(PrecompileOutput { - exit_status: ExitSucceed::Returned, - output: vec![], - }), - Err(_) => Err(PrecompileFailure::Error { - exit_status: ExitError::Other("Subtensor call failed".into()), - }), - } - } - - fn transfer_back_to_caller( - account_id: &AccountId32, - amount: U256, - ) -> Result<(), PrecompileFailure> { - // this is staking smart contract's(0x0000000000000000000000000000000000000801) sr25519 address - let smart_contract_account_id = - match AccountId32::from_ss58check("5CwnBK9Ack1mhznmCnwiibCNQc174pYQVktYW3ayRpLm4K2X") { - Ok(addr) => addr, - Err(_) => { - return Err(PrecompileFailure::Error { - exit_status: ExitError::Other("Invalid SS58 address".into()), - }); - } - }; - let amount_sub = - <Runtime as pallet_evm::Config>::BalanceConverter::into_substrate_balance(amount) - .ok_or(ExitError::OutOfFund)?; - - // Create a transfer call from the smart contract to the caller - let transfer_call = - RuntimeCall::Balances(pallet_balances::Call::<Runtime>::transfer_allow_death { - dest: account_id.clone().into(), - value: amount_sub.unique_saturated_into(), - }); - - // Execute the transfer - let transfer_result = - transfer_call.dispatch(RawOrigin::Signed(smart_contract_account_id).into()); - - if let Err(dispatch_error) = transfer_result { - log::error!( - "Transfer back to caller failed. Error: {:?}", - dispatch_error - ); - return Err(PrecompileFailure::Error { - exit_status: ExitError::Other("Transfer back to caller failed".into()), - }); - } - - Ok(()) - } + // fn dispatch(handle: &mut impl PrecompileHandle, call: RuntimeCall) -> PrecompileResult { + // let account_id = + // <HashedAddressMapping<BlakeTwo256> as AddressMapping<AccountId32>>::into_account_id( + // handle.context().caller, + // ); + + // // Transfer the amount back to the caller before executing the staking operation + // // let caller = handle.context().caller; + // let amount = handle.context().apparent_value; + + // if !amount.is_zero() { + // Self::transfer_back_to_caller(&account_id, amount)?; + // } + + // let result = call.dispatch(RawOrigin::Signed(account_id.clone()).into()); + // match &result { + // Ok(post_info) => log::info!("Dispatch succeeded. Post info: {:?}", post_info), + // Err(dispatch_error) => log::error!("Dispatch failed. Error: {:?}", dispatch_error), + // } + // match result { + // Ok(_) => Ok(PrecompileOutput { + // exit_status: ExitSucceed::Returned, + // output: vec![], + // }), + // Err(_) => Err(PrecompileFailure::Error { + // exit_status: ExitError::Other("Subtensor call failed".into()), + // }), + // } + // } + + // fn transfer_back_to_caller( + // account_id: &AccountId32, + // amount: U256, + // ) -> Result<(), PrecompileFailure> { + // // this is staking smart contract's(0x0000000000000000000000000000000000000801) sr25519 address + // let smart_contract_account_id = + // match AccountId32::from_ss58check("5CwnBK9Ack1mhznmCnwiibCNQc174pYQVktYW3ayRpLm4K2X") { + // Ok(addr) => addr, + // Err(_) => { + // return Err(PrecompileFailure::Error { + // exit_status: ExitError::Other("Invalid SS58 address".into()), + // }); + // } + // }; + // let amount_sub = + // <Runtime as pallet_evm::Config>::BalanceConverter::into_substrate_balance(amount) + // .ok_or(ExitError::OutOfFund)?; + + // // Create a transfer call from the smart contract to the caller + // let transfer_call = + // RuntimeCall::Balances(pallet_balances::Call::<Runtime>::transfer_allow_death { + // dest: account_id.clone().into(), + // value: amount_sub.unique_saturated_into(), + // }); + + // // Execute the transfer + // let transfer_result = + // transfer_call.dispatch(RawOrigin::Signed(smart_contract_account_id).into()); + + // if let Err(dispatch_error) = transfer_result { + // log::error!( + // "Transfer back to caller failed. Error: {:?}", + // dispatch_error + // ); + // return Err(PrecompileFailure::Error { + // exit_status: ExitError::Other("Transfer back to caller failed".into()), + // }); + // } + + // Ok(()) + // } } diff --git a/runtime/src/precompiles/subnet.rs b/runtime/src/precompiles/subnet.rs new file mode 100644 index 000000000..a74bbf53a --- /dev/null +++ b/runtime/src/precompiles/subnet.rs @@ -0,0 +1,130 @@ +use crate::precompiles::{dispatch, get_method_id, get_slice}; +use crate::{Runtime, RuntimeCall}; +use pallet_evm::{ExitError, PrecompileFailure, PrecompileHandle, PrecompileResult}; +use sp_std::vec; + +pub const SUBNET_PRECOMPILE_INDEX: u64 = 2051; +// three bytes with max lenght 1K +pub const MAX_PARAMETER_SIZE: usize = 3 * 1024; + +pub struct SubnetPrecompile; + +impl SubnetPrecompile { + pub fn execute(handle: &mut impl PrecompileHandle) -> PrecompileResult { + let txdata = handle.input(); + if txdata.len() > MAX_PARAMETER_SIZE { + log::error!("the length of subnet call is {} ", txdata.len()); + return Err(PrecompileFailure::Error { + exit_status: ExitError::InvalidRange, + }); + } + let method_id = get_slice(txdata, 0, 4)?; + let method_input = txdata + .get(4..) + .map_or_else(vec::Vec::new, |slice| slice.to_vec()); // Avoiding borrowing conflicts + + match method_id { + id if id == get_method_id("registerNetwork(bytes,bytes,bytes)") => { + Self::register_network(handle, &method_input) + } + id if id == get_method_id("registerNetwork()") => { + Self::register_network(handle, &[0_u8; 0]) + } + _ => Err(PrecompileFailure::Error { + exit_status: ExitError::InvalidRange, + }), + } + } + + fn register_network(handle: &mut impl PrecompileHandle, data: &[u8]) -> PrecompileResult { + let call = if data.is_empty() { + RuntimeCall::SubtensorModule( + pallet_subtensor::Call::<Runtime>::register_network_with_identity { + identity: None, + }, + ) + } else { + let (subnet_name, github_repo, subnet_contact) = + Self::parse_register_network_parameters(data)?; + + let identity: pallet_subtensor::SubnetIdentityOf = pallet_subtensor::SubnetIdentityOf { + subnet_name, + github_repo, + subnet_contact, + }; + + // Create the register_network callcle + RuntimeCall::SubtensorModule( + pallet_subtensor::Call::<Runtime>::register_network_with_identity { + identity: Some(identity), + }, + ) + }; + + // Dispatch the register_network call + dispatch(handle, call) + } + + fn parse_register_network_parameters( + data: &[u8], + ) -> Result<(vec::Vec<u8>, vec::Vec<u8>, vec::Vec<u8>), PrecompileFailure> { + let mut buf = [0_u8; 4]; + + // get all start point for three data items: name, repo and contact + buf.copy_from_slice(get_slice(data, 28, 32)?); + let subnet_name_start: usize = u32::from_be_bytes(buf) as usize; + + buf.copy_from_slice(get_slice(data, 60, 64)?); + let github_repo_start: usize = u32::from_be_bytes(buf) as usize; + + buf.copy_from_slice(get_slice(data, 92, 96)?); + let subnet_contact_start: usize = u32::from_be_bytes(buf) as usize; + + // get name + buf.copy_from_slice(get_slice( + data, + subnet_name_start + 28, + subnet_name_start + 32, + )?); + let subnet_name_len: usize = u32::from_be_bytes(buf) as usize; + + let mut name_vec = vec![0; subnet_name_len]; + name_vec.copy_from_slice(get_slice( + data, + subnet_name_start + 32, + subnet_name_start + subnet_name_len + 32, + )?); + + // get repo data + buf.copy_from_slice(get_slice( + data, + github_repo_start + 28, + github_repo_start + 32, + )?); + let github_repo_len: usize = u32::from_be_bytes(buf) as usize; + + let mut repo_vec = vec![0; github_repo_len]; + repo_vec.copy_from_slice(get_slice( + data, + github_repo_start + 32, + github_repo_start + github_repo_len + 32, + )?); + + // get contact data + buf.copy_from_slice(get_slice( + data, + subnet_contact_start + 28, + subnet_contact_start + 32, + )?); + let subnet_contact_len: usize = u32::from_be_bytes(buf) as usize; + + let mut contact_vec = vec![0; subnet_contact_len]; + contact_vec.copy_from_slice(get_slice( + data, + subnet_contact_start + 32, + subnet_contact_start + subnet_contact_len + 32, + )?); + + Ok((name_vec, repo_vec, contact_vec)) + } +} From 157032bc03c1341adb5651ec481902584572034a Mon Sep 17 00:00:00 2001 From: open-junius <zhou@opentensor.dev> Date: Tue, 10 Dec 2024 23:55:11 +0800 Subject: [PATCH 002/145] remove commented code --- runtime/src/precompiles/staking.rs | 72 ------------------------------ 1 file changed, 72 deletions(-) diff --git a/runtime/src/precompiles/staking.rs b/runtime/src/precompiles/staking.rs index fc1e7b512..e8d33c05e 100644 --- a/runtime/src/precompiles/staking.rs +++ b/runtime/src/precompiles/staking.rs @@ -105,76 +105,4 @@ impl StakingPrecompile { hotkey.copy_from_slice(get_slice(data, 0, 32)?); Ok(hotkey) } - - // fn dispatch(handle: &mut impl PrecompileHandle, call: RuntimeCall) -> PrecompileResult { - // let account_id = - // <HashedAddressMapping<BlakeTwo256> as AddressMapping<AccountId32>>::into_account_id( - // handle.context().caller, - // ); - - // // Transfer the amount back to the caller before executing the staking operation - // // let caller = handle.context().caller; - // let amount = handle.context().apparent_value; - - // if !amount.is_zero() { - // Self::transfer_back_to_caller(&account_id, amount)?; - // } - - // let result = call.dispatch(RawOrigin::Signed(account_id.clone()).into()); - // match &result { - // Ok(post_info) => log::info!("Dispatch succeeded. Post info: {:?}", post_info), - // Err(dispatch_error) => log::error!("Dispatch failed. Error: {:?}", dispatch_error), - // } - // match result { - // Ok(_) => Ok(PrecompileOutput { - // exit_status: ExitSucceed::Returned, - // output: vec![], - // }), - // Err(_) => Err(PrecompileFailure::Error { - // exit_status: ExitError::Other("Subtensor call failed".into()), - // }), - // } - // } - - // fn transfer_back_to_caller( - // account_id: &AccountId32, - // amount: U256, - // ) -> Result<(), PrecompileFailure> { - // // this is staking smart contract's(0x0000000000000000000000000000000000000801) sr25519 address - // let smart_contract_account_id = - // match AccountId32::from_ss58check("5CwnBK9Ack1mhznmCnwiibCNQc174pYQVktYW3ayRpLm4K2X") { - // Ok(addr) => addr, - // Err(_) => { - // return Err(PrecompileFailure::Error { - // exit_status: ExitError::Other("Invalid SS58 address".into()), - // }); - // } - // }; - // let amount_sub = - // <Runtime as pallet_evm::Config>::BalanceConverter::into_substrate_balance(amount) - // .ok_or(ExitError::OutOfFund)?; - - // // Create a transfer call from the smart contract to the caller - // let transfer_call = - // RuntimeCall::Balances(pallet_balances::Call::<Runtime>::transfer_allow_death { - // dest: account_id.clone().into(), - // value: amount_sub.unique_saturated_into(), - // }); - - // // Execute the transfer - // let transfer_result = - // transfer_call.dispatch(RawOrigin::Signed(smart_contract_account_id).into()); - - // if let Err(dispatch_error) = transfer_result { - // log::error!( - // "Transfer back to caller failed. Error: {:?}", - // dispatch_error - // ); - // return Err(PrecompileFailure::Error { - // exit_status: ExitError::Other("Transfer back to caller failed".into()), - // }); - // } - - // Ok(()) - // } } From 5435ccd467dd53a8200285c52550070a5ee05801 Mon Sep 17 00:00:00 2001 From: open-junius <zhou@opentensor.dev> Date: Wed, 11 Dec 2024 00:17:09 +0800 Subject: [PATCH 003/145] refactor code --- runtime/src/precompiles/mod.rs | 28 +++++++++++++++++----------- runtime/src/precompiles/staking.rs | 8 +++++--- runtime/src/precompiles/subnet.rs | 6 +++++- 3 files changed, 27 insertions(+), 15 deletions(-) diff --git a/runtime/src/precompiles/mod.rs b/runtime/src/precompiles/mod.rs index ff8efa599..7f54d2e3c 100644 --- a/runtime/src/precompiles/mod.rs +++ b/runtime/src/precompiles/mod.rs @@ -146,20 +146,22 @@ pub fn get_slice(data: &[u8], from: usize, to: usize) -> Result<&[u8], Precompil } } +/// The function return the token to smart contract fn transfer_back_to_caller( + smart_contract_address: &str, account_id: &AccountId32, amount: U256, ) -> Result<(), PrecompileFailure> { // this is staking smart contract's(0x0000000000000000000000000000000000000801) sr25519 address - let smart_contract_account_id = - match AccountId32::from_ss58check("5CwnBK9Ack1mhznmCnwiibCNQc174pYQVktYW3ayRpLm4K2X") { - Ok(addr) => addr, - Err(_) => { - return Err(PrecompileFailure::Error { - exit_status: ExitError::Other("Invalid SS58 address".into()), - }); - } - }; + let smart_contract_account_id = match AccountId32::from_ss58check(smart_contract_address) { + // match AccountId32::from_ss58check("5CwnBK9Ack1mhznmCnwiibCNQc174pYQVktYW3ayRpLm4K2X") { + Ok(addr) => addr, + Err(_) => { + return Err(PrecompileFailure::Error { + exit_status: ExitError::Other("Invalid SS58 address".into()), + }); + } + }; let amount_sub = <Runtime as pallet_evm::Config>::BalanceConverter::into_substrate_balance(amount) .ok_or(ExitError::OutOfFund)?; @@ -188,7 +190,11 @@ fn transfer_back_to_caller( Ok(()) } -fn dispatch(handle: &mut impl PrecompileHandle, call: RuntimeCall) -> PrecompileResult { +fn dispatch( + handle: &mut impl PrecompileHandle, + call: RuntimeCall, + smart_contract_address: &str, +) -> PrecompileResult { let account_id = <HashedAddressMapping<BlakeTwo256> as AddressMapping<AccountId32>>::into_account_id( handle.context().caller, @@ -199,7 +205,7 @@ fn dispatch(handle: &mut impl PrecompileHandle, call: RuntimeCall) -> Precompile let amount = handle.context().apparent_value; if !amount.is_zero() { - transfer_back_to_caller(&account_id, amount)?; + transfer_back_to_caller(smart_contract_address, &account_id, amount)?; } let result = call.dispatch(RawOrigin::Signed(account_id.clone()).into()); diff --git a/runtime/src/precompiles/staking.rs b/runtime/src/precompiles/staking.rs index e8d33c05e..9454485ff 100644 --- a/runtime/src/precompiles/staking.rs +++ b/runtime/src/precompiles/staking.rs @@ -35,7 +35,9 @@ use sp_std::vec; use crate::{Runtime, RuntimeCall}; pub const STAKING_PRECOMPILE_INDEX: u64 = 2049; - +// this is staking smart contract's(0x0000000000000000000000000000000000000801) sr25519 address +pub const STAKING_CONTRACT_ADDRESS: &'static str = + "5CwnBK9Ack1mhznmCnwiibCNQc174pYQVktYW3ayRpLm4K2X"; pub struct StakingPrecompile; impl StakingPrecompile { @@ -72,7 +74,7 @@ impl StakingPrecompile { amount_staked: amount_sub.unique_saturated_into(), }); // Dispatch the add_stake call - dispatch(handle, call) + dispatch(handle, call, STAKING_CONTRACT_ADDRESS) } fn remove_stake(handle: &mut impl PrecompileHandle, data: &[u8]) -> PrecompileResult { let hotkey = Self::parse_hotkey(data)?.into(); @@ -92,7 +94,7 @@ impl StakingPrecompile { hotkey, amount_unstaked: amount_sub.unique_saturated_into(), }); - dispatch(handle, call) + dispatch(handle, call, STAKING_CONTRACT_ADDRESS) } fn parse_hotkey(data: &[u8]) -> Result<[u8; 32], PrecompileFailure> { diff --git a/runtime/src/precompiles/subnet.rs b/runtime/src/precompiles/subnet.rs index a74bbf53a..f68e089b3 100644 --- a/runtime/src/precompiles/subnet.rs +++ b/runtime/src/precompiles/subnet.rs @@ -7,6 +7,10 @@ pub const SUBNET_PRECOMPILE_INDEX: u64 = 2051; // three bytes with max lenght 1K pub const MAX_PARAMETER_SIZE: usize = 3 * 1024; +// this is staking smart contract's(0x0000000000000000000000000000000000000803) sr25519 address +pub const STAKING_CONTRACT_ADDRESS: &'static str = + "5DPSUCb5mZFfizvBDSnRoAqmxV5Bmov2CS3xV773qU6VP1w2"; + pub struct SubnetPrecompile; impl SubnetPrecompile { @@ -62,7 +66,7 @@ impl SubnetPrecompile { }; // Dispatch the register_network call - dispatch(handle, call) + dispatch(handle, call, STAKING_CONTRACT_ADDRESS) } fn parse_register_network_parameters( From 71699085a6d9b554630abb4d9d19e54c607d33c1 Mon Sep 17 00:00:00 2001 From: open-junius <zhou@opentensor.dev> Date: Wed, 11 Dec 2024 09:42:35 +0800 Subject: [PATCH 004/145] fix clippy --- runtime/src/precompiles/staking.rs | 3 +-- runtime/src/precompiles/subnet.rs | 3 +-- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/runtime/src/precompiles/staking.rs b/runtime/src/precompiles/staking.rs index 9454485ff..31c692cd6 100644 --- a/runtime/src/precompiles/staking.rs +++ b/runtime/src/precompiles/staking.rs @@ -36,8 +36,7 @@ use sp_std::vec; use crate::{Runtime, RuntimeCall}; pub const STAKING_PRECOMPILE_INDEX: u64 = 2049; // this is staking smart contract's(0x0000000000000000000000000000000000000801) sr25519 address -pub const STAKING_CONTRACT_ADDRESS: &'static str = - "5CwnBK9Ack1mhznmCnwiibCNQc174pYQVktYW3ayRpLm4K2X"; +pub const STAKING_CONTRACT_ADDRESS: &str = "5CwnBK9Ack1mhznmCnwiibCNQc174pYQVktYW3ayRpLm4K2X"; pub struct StakingPrecompile; impl StakingPrecompile { diff --git a/runtime/src/precompiles/subnet.rs b/runtime/src/precompiles/subnet.rs index f68e089b3..9230a01e9 100644 --- a/runtime/src/precompiles/subnet.rs +++ b/runtime/src/precompiles/subnet.rs @@ -8,8 +8,7 @@ pub const SUBNET_PRECOMPILE_INDEX: u64 = 2051; pub const MAX_PARAMETER_SIZE: usize = 3 * 1024; // this is staking smart contract's(0x0000000000000000000000000000000000000803) sr25519 address -pub const STAKING_CONTRACT_ADDRESS: &'static str = - "5DPSUCb5mZFfizvBDSnRoAqmxV5Bmov2CS3xV773qU6VP1w2"; +pub const STAKING_CONTRACT_ADDRESS: &str = "5DPSUCb5mZFfizvBDSnRoAqmxV5Bmov2CS3xV773qU6VP1w2"; pub struct SubnetPrecompile; From cdda2f68ac3d50dc2f9c3631328f4bd8f6abbe90 Mon Sep 17 00:00:00 2001 From: open-junius <zhou@opentensor.dev> Date: Wed, 11 Dec 2024 10:59:41 +0800 Subject: [PATCH 005/145] fix compile error --- runtime/src/precompiles/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/runtime/src/precompiles/mod.rs b/runtime/src/precompiles/mod.rs index 4d8abdac2..398a80a05 100644 --- a/runtime/src/precompiles/mod.rs +++ b/runtime/src/precompiles/mod.rs @@ -53,7 +53,7 @@ where pub fn new() -> Self { Self(Default::default()) } - pub fn used_addresses() -> [H160; 11] { + pub fn used_addresses() -> [H160; 12] { [ hash(1), hash(2), From e565825b2a0b852d614bc5496f5d43b17763552d Mon Sep 17 00:00:00 2001 From: open-junius <zhou@opentensor.dev> Date: Mon, 16 Dec 2024 20:30:54 +0800 Subject: [PATCH 006/145] add memory storage --- runtime/src/precompiles/solidity/subnet.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/runtime/src/precompiles/solidity/subnet.sol b/runtime/src/precompiles/solidity/subnet.sol index e2857ad63..581788b03 100644 --- a/runtime/src/precompiles/solidity/subnet.sol +++ b/runtime/src/precompiles/solidity/subnet.sol @@ -6,5 +6,5 @@ interface ISubnet { /// Registers a new network without specifying details. function registerNetwork() external payable; /// Registers a new network with specified subnet name, GitHub repository, and contact information. - function registerNetwork(bytes subnetName, bytes githubRepo, bytes subnetContact) external payable; + function registerNetwork(bytes memory subnetName, bytes memory githubRepo, bytes memory subnetContact) external payable; } \ No newline at end of file From 6a0f6f91accff4748b1ccafe258ca6f1a44536e8 Mon Sep 17 00:00:00 2001 From: open-junius <zhou@opentensor.dev> Date: Fri, 20 Dec 2024 18:33:52 +0800 Subject: [PATCH 007/145] add subnets precompile --- runtime/src/precompiles/mod.rs | 6 +- runtime/src/precompiles/solidity/staking.sol | 44 +++++++------ runtime/src/precompiles/solidity/subnets.abi | 20 ++++++ runtime/src/precompiles/solidity/subnets.sol | 14 ++++ runtime/src/precompiles/subnets.rs | 68 ++++++++++++++++++++ 5 files changed, 131 insertions(+), 21 deletions(-) create mode 100644 runtime/src/precompiles/solidity/subnets.abi create mode 100644 runtime/src/precompiles/solidity/subnets.sol create mode 100644 runtime/src/precompiles/subnets.rs diff --git a/runtime/src/precompiles/mod.rs b/runtime/src/precompiles/mod.rs index e13516e95..20d013194 100644 --- a/runtime/src/precompiles/mod.rs +++ b/runtime/src/precompiles/mod.rs @@ -15,11 +15,13 @@ mod balance_transfer; mod ed25519; mod metagraph; mod staking; +mod subnets; use balance_transfer::*; use ed25519::*; use metagraph::*; use staking::*; +use subnets::*; pub struct FrontierPrecompiles<R>(PhantomData<R>); @@ -39,7 +41,7 @@ where pub fn new() -> Self { Self(Default::default()) } - pub fn used_addresses() -> [H160; 11] { + pub fn used_addresses() -> [H160; 12] { [ hash(1), hash(2), @@ -52,6 +54,7 @@ where hash(BALANCE_TRANSFER_INDEX), hash(STAKING_PRECOMPILE_INDEX), hash(METAGRAPH_PRECOMPILE_INDEX), + hash(SUBNETS_PRECOMPILE_INDEX), ] } } @@ -79,6 +82,7 @@ where a if a == hash(METAGRAPH_PRECOMPILE_INDEX) => { Some(MetagraphPrecompile::execute(handle)) } + a if a == hash(SUBNETS_PRECOMPILE_INDEX) => Some(SubnetsPrecompile::execute(handle)), _ => None, } diff --git a/runtime/src/precompiles/solidity/staking.sol b/runtime/src/precompiles/solidity/staking.sol index ec7fb7297..057fa2dce 100644 --- a/runtime/src/precompiles/solidity/staking.sol +++ b/runtime/src/precompiles/solidity/staking.sol @@ -3,26 +3,26 @@ pragma solidity ^0.8.0; address constant ISTAKING_ADDRESS = 0x0000000000000000000000000000000000000801; interface IStaking { - /** - * @dev Adds a subtensor stake corresponding to the value sent with the transaction, associated - * with the `hotkey`. - * - * This function allows external accounts and contracts to stake TAO into the subtensor pallet, - * which effectively calls `add_stake` on the subtensor pallet with specified hotkey as a parameter - * and coldkey being the hashed address mapping of H160 sender address to Substrate ss58 address as - * implemented in Frontier HashedAddressMapping: - * https://github.com/polkadot-evm/frontier/blob/2e219e17a526125da003e64ef22ec037917083fa/frame/evm/src/lib.rs#L739 - * - * @param hotkey The hotkey public key (32 bytes). - * @param netuid The subnet to stake to (uint16). Currently a noop, functionality will be enabled with RAO. - * - * Requirements: - * - `hotkey` must be a valid hotkey registered on the network, ensuring that the stake is - * correctly attributed. - */ - function addStake(bytes32 hotkey, uint16 netuid) external payable; + /** + * @dev Adds a subtensor stake corresponding to the value sent with the transaction, associated + * with the `hotkey`. + * + * This function allows external accounts and contracts to stake TAO into the subtensor pallet, + * which effectively calls `add_stake` on the subtensor pallet with specified hotkey as a parameter + * and coldkey being the hashed address mapping of H160 sender address to Substrate ss58 address as + * implemented in Frontier HashedAddressMapping: + * https://github.com/polkadot-evm/frontier/blob/2e219e17a526125da003e64ef22ec037917083fa/frame/evm/src/lib.rs#L739 + * + * @param hotkey The hotkey public key (32 bytes). + * @param netuid The subnet to stake to (uint16). Currently a noop, functionality will be enabled with RAO. + * + * Requirements: + * - `hotkey` must be a valid hotkey registered on the network, ensuring that the stake is + * correctly attributed. + */ + function addStake(bytes32 hotkey, uint16 netuid) external payable; - /** + /** * @dev Removes a subtensor stake `amount` from the specified `hotkey`. * * This function allows external accounts and contracts to unstake TAO from the subtensor pallet, @@ -41,5 +41,9 @@ interface IStaking { * correctly attributed. * - The existing stake amount must be not lower than specified amount */ - function removeStake(bytes32 hotkey, uint256 amount, uint16 netuid) external; + function removeStake( + bytes32 hotkey, + uint256 amount, + uint16 netuid + ) external; } diff --git a/runtime/src/precompiles/solidity/subnets.abi b/runtime/src/precompiles/solidity/subnets.abi new file mode 100644 index 000000000..09a14eb2d --- /dev/null +++ b/runtime/src/precompiles/solidity/subnets.abi @@ -0,0 +1,20 @@ +[ + { + "inputs": [ + { + "internalType": "uint16", + "name": "netuid", + "type": "uint16" + }, + { + "internalType": "bytes32", + "name": "hotkey", + "type": "bytes32" + } + ], + "name": "burnedRegister", + "outputs": [], + "stateMutability": "payable", + "type": "function" + } +] diff --git a/runtime/src/precompiles/solidity/subnets.sol b/runtime/src/precompiles/solidity/subnets.sol new file mode 100644 index 000000000..5cbf72779 --- /dev/null +++ b/runtime/src/precompiles/solidity/subnets.sol @@ -0,0 +1,14 @@ +pragma solidity ^0.8.0; + +address constant ISUBNETS_ADDRESS = 0x0000000000000000000000000000000000000804; + +interface ISubnets { + /** + * @dev Registers a neuron by calling `do_burned_registration` internally with the origin set to the ss58 mirror of the H160 address. + * This allows the H160 to further call neuron-related methods and receive emissions. + * + * @param netuid The subnet to register the neuron to (uint16). + * @param hotkey The hotkey public key (32 bytes). + */ + function burnedRegister(uint16 netuid, bytes32 hotkey) external payable; +} diff --git a/runtime/src/precompiles/subnets.rs b/runtime/src/precompiles/subnets.rs new file mode 100644 index 000000000..ffc0c71a0 --- /dev/null +++ b/runtime/src/precompiles/subnets.rs @@ -0,0 +1,68 @@ +use pallet_evm::{ + ExitError, ExitSucceed, PrecompileFailure, PrecompileHandle, PrecompileOutput, PrecompileResult, +}; + +use crate::precompiles::{get_method_id, get_slice}; +use sp_std::vec; + +use crate::{Runtime, RuntimeCall}; +pub const SUBNETS_PRECOMPILE_INDEX: u64 = 2052; + +// this is subnets smart contract's(0x0000000000000000000000000000000000000804) sr25519 address +pub const SUBNETS_CONTRACT_ADDRESS: &str = "5GKZiUUgTnWSz3BgiVBMehEKkLszsG4ZXnvgWpWFUFKqrqyn"; + +pub struct SubnetsPrecompile; + +impl SubnetsPrecompile { + pub fn execute(handle: &mut impl PrecompileHandle) -> PrecompileResult { + let txdata = handle.input(); + let method_id = get_slice(txdata, 0, 4)?; + let method_input = txdata + .get(4..) + .map_or_else(vec::Vec::new, |slice| slice.to_vec()); // Avoiding borrowing conflicts + + match method_id { + id if id == get_method_id("burnedRegister(uint16,bytes32)") => { + Self::burned_register(handle, &method_input) + } + + _ => Err(PrecompileFailure::Error { + exit_status: ExitError::InvalidRange, + }), + } + } + + pub fn burned_register(handle: &mut impl PrecompileHandle, data: &[u8]) -> PrecompileResult { + let (netuid, hotkey) = Self::parse_netuid_hotkey_parameter(data)?; + let call = + RuntimeCall::SubtensorModule(pallet_subtensor::Call::<Runtime>::burned_register { + netuid, + hotkey: hotkey.into(), + }); + Self::dispatch(handle, call) + } + + fn parse_netuid_hotkey_parameter(data: &[u8]) -> Result<(u16, [u8; 32]), PrecompileFailure> { + if data.len() < 64 { + return Err(PrecompileFailure::Error { + exit_status: ExitError::InvalidRange, + }); + } + let mut netuid_vec = [0u8; 2]; + netuid_vec.copy_from_slice(get_slice(data, 30, 32)?); + let netuid = u16::from_be_bytes(netuid_vec); + + let mut parameter = [0u8; 32]; + parameter.copy_from_slice(get_slice(data, 32, 64)?); + + Ok((netuid, parameter)) + } + + // will remove it after merge with other PR + fn dispatch(_handle: &mut impl PrecompileHandle, _call: RuntimeCall) -> PrecompileResult { + Ok(PrecompileOutput { + exit_status: ExitSucceed::Returned, + output: vec![], + }) + } +} From 0ff5f9a136dd681c79abfa40acb40784b81628cc Mon Sep 17 00:00:00 2001 From: Cameron Fairchild <cameron@opentensor.ai> Date: Mon, 13 Jan 2025 20:07:17 -0500 Subject: [PATCH 008/145] wip --- pallets/subtensor/src/coinbase/root.rs | 5 ++ pallets/subtensor/src/macros/hooks.rs | 4 +- .../migrate_fix_is_network_member.rs | 58 +++++++++++++++++++ pallets/subtensor/src/migrations/mod.rs | 1 + 4 files changed, 67 insertions(+), 1 deletion(-) create mode 100644 pallets/subtensor/src/migrations/migrate_fix_is_network_member.rs diff --git a/pallets/subtensor/src/coinbase/root.rs b/pallets/subtensor/src/coinbase/root.rs index fee6e672b..c4fc1a9f9 100644 --- a/pallets/subtensor/src/coinbase/root.rs +++ b/pallets/subtensor/src/coinbase/root.rs @@ -527,6 +527,7 @@ impl<T: Config> Pallet<T> { // --- 7. Remove incentive mechanism memory. let _ = Uids::<T>::clear_prefix(netuid, u32::MAX, None); + let keys = Keys::<T>::iter_prefix(netuid).collect::<Vec<_>>(); let _ = Keys::<T>::clear_prefix(netuid, u32::MAX, None); let _ = Bonds::<T>::clear_prefix(netuid, u32::MAX, None); @@ -564,6 +565,10 @@ impl<T: Config> Pallet<T> { ValidatorPermit::<T>::remove(netuid); ValidatorTrust::<T>::remove(netuid); + for key in keys { + IsNetworkMember::<T>::remove(key, netuid); + } + // --- 11. Erase network parameters. Tempo::<T>::remove(netuid); Kappa::<T>::remove(netuid); diff --git a/pallets/subtensor/src/macros/hooks.rs b/pallets/subtensor/src/macros/hooks.rs index bf255c1c7..2a43238ab 100644 --- a/pallets/subtensor/src/macros/hooks.rs +++ b/pallets/subtensor/src/macros/hooks.rs @@ -74,7 +74,9 @@ mod hooks { // Migrate Commit-Reval 2.0 .saturating_add(migrations::migrate_commit_reveal_v2::migrate_commit_reveal_2::<T>()) // Migrate to RAO - .saturating_add(migrations::migrate_rao::migrate_rao::<T>()); + .saturating_add(migrations::migrate_rao::migrate_rao::<T>()) + // Fix the IsNetworkMember map to be consistent with other storage maps + .saturating_add(migrations::migrate_fix_is_network_member::migrate_fix_is_network_member::<T>()); weight } diff --git a/pallets/subtensor/src/migrations/migrate_fix_is_network_member.rs b/pallets/subtensor/src/migrations/migrate_fix_is_network_member.rs new file mode 100644 index 000000000..0225cd8fb --- /dev/null +++ b/pallets/subtensor/src/migrations/migrate_fix_is_network_member.rs @@ -0,0 +1,58 @@ +use super::*; +use alloc::string::String; +use frame_support::{traits::Get, weights::Weight}; +use log; + +pub fn migrate_fix_is_network_member<T: Config>() -> Weight { + let migration_name = b"migrate_fix_is_network_member".to_vec(); + + // Initialize the weight with one read operation. + let mut weight = T::DbWeight::get().reads(1); + + // Check if the migration has already run + if HasMigrationRun::<T>::get(&migration_name) { + log::info!( + "Migration '{:?}' has already run. Skipping.", + migration_name + ); + return weight; + } + log::info!( + "Running migration '{}'", + String::from_utf8_lossy(&migration_name) + ); + + weight = do_fix_is_network_member::<T>(weight); + + // Mark the migration as completed + HasMigrationRun::<T>::insert(&migration_name, true); + weight = weight.saturating_add(T::DbWeight::get().writes(1)); + + log::info!( + "Migration '{:?}' completed. Storage version set to 7.", + String::from_utf8_lossy(&migration_name) + ); + + // Return the migration weight. + weight +} + +fn do_fix_is_network_member<T: Config>(weight: Weight) -> Weight { + // Clear the IsNetworkMember storage + let mut curr = IsNetworkMember::<T>::clear(U32::MAX, None); + weight = weight.saturating_add(T::DbWeight::get().reads_writes(curr.loops, curr.unique)); + while curr.maybe_cursor.is_some() { + // Clear until empty + curr = IsNetworkMember::<T>::clear(U32::MAX, curr.maybe_cursor); + weight = weight.saturating_add(T::DbWeight::get().reads_writes(curr.loops, curr.unique)); + } + // Repopulate the IsNetworkMember storage using the Keys map + let netuids = Subtensor::<T>::get_all_subnet_netuids(); + for netuid in netuids { + for key in Keys::<T>::iter_prefix(netuid) { + IsNetworkMember::<T>::insert(key, netuid, true); + } + } + + weight +} diff --git a/pallets/subtensor/src/migrations/mod.rs b/pallets/subtensor/src/migrations/mod.rs index dc9011f3e..6cf358c4d 100644 --- a/pallets/subtensor/src/migrations/mod.rs +++ b/pallets/subtensor/src/migrations/mod.rs @@ -4,6 +4,7 @@ pub mod migrate_commit_reveal_v2; pub mod migrate_create_root_network; pub mod migrate_delete_subnet_21; pub mod migrate_delete_subnet_3; +pub mod migrate_fix_is_network_member; pub mod migrate_fix_total_coldkey_stake; pub mod migrate_init_total_issuance; pub mod migrate_populate_owned_hotkeys; From 53e78f94cbc256aff8d7984f675e8a5efe318fc5 Mon Sep 17 00:00:00 2001 From: camfairchild <cameron@opentensor.dev> Date: Mon, 13 Jan 2025 21:31:42 -0500 Subject: [PATCH 009/145] fix syntax --- pallets/subtensor/src/coinbase/root.rs | 2 +- .../migrate_fix_is_network_member.rs | 19 ++++++++++--------- 2 files changed, 11 insertions(+), 10 deletions(-) diff --git a/pallets/subtensor/src/coinbase/root.rs b/pallets/subtensor/src/coinbase/root.rs index c4fc1a9f9..112c88785 100644 --- a/pallets/subtensor/src/coinbase/root.rs +++ b/pallets/subtensor/src/coinbase/root.rs @@ -565,7 +565,7 @@ impl<T: Config> Pallet<T> { ValidatorPermit::<T>::remove(netuid); ValidatorTrust::<T>::remove(netuid); - for key in keys { + for (_uid, key) in keys { IsNetworkMember::<T>::remove(key, netuid); } diff --git a/pallets/subtensor/src/migrations/migrate_fix_is_network_member.rs b/pallets/subtensor/src/migrations/migrate_fix_is_network_member.rs index 0225cd8fb..3c89717f1 100644 --- a/pallets/subtensor/src/migrations/migrate_fix_is_network_member.rs +++ b/pallets/subtensor/src/migrations/migrate_fix_is_network_member.rs @@ -38,20 +38,21 @@ pub fn migrate_fix_is_network_member<T: Config>() -> Weight { } fn do_fix_is_network_member<T: Config>(weight: Weight) -> Weight { + let mut weight = weight; // Clear the IsNetworkMember storage - let mut curr = IsNetworkMember::<T>::clear(U32::MAX, None); - weight = weight.saturating_add(T::DbWeight::get().reads_writes(curr.loops, curr.unique)); + let mut curr = IsNetworkMember::<T>::clear(u32::MAX, None); + weight = weight + .saturating_add(T::DbWeight::get().reads_writes(curr.loops as u64, curr.unique as u64)); while curr.maybe_cursor.is_some() { // Clear until empty - curr = IsNetworkMember::<T>::clear(U32::MAX, curr.maybe_cursor); - weight = weight.saturating_add(T::DbWeight::get().reads_writes(curr.loops, curr.unique)); + curr = IsNetworkMember::<T>::clear(u32::MAX, curr.maybe_cursor.as_deref()); + weight = weight + .saturating_add(T::DbWeight::get().reads_writes(curr.loops as u64, curr.unique as u64)); } // Repopulate the IsNetworkMember storage using the Keys map - let netuids = Subtensor::<T>::get_all_subnet_netuids(); - for netuid in netuids { - for key in Keys::<T>::iter_prefix(netuid) { - IsNetworkMember::<T>::insert(key, netuid, true); - } + for (netuid, _uid, key) in Keys::<T>::iter() { + IsNetworkMember::<T>::insert(key, netuid, true); + weight = weight.saturating_add(T::DbWeight::get().reads_writes(1, 1)); } weight From 164323825311e1a23918cb94fefcd1221e2061a7 Mon Sep 17 00:00:00 2001 From: open-junius <zhou@opentensor.dev> Date: Thu, 16 Jan 2025 20:41:55 +0800 Subject: [PATCH 010/145] fix compilation --- runtime/src/precompiles/subnet.rs | 33 +++++++++++++++++++++++-------- 1 file changed, 25 insertions(+), 8 deletions(-) diff --git a/runtime/src/precompiles/subnet.rs b/runtime/src/precompiles/subnet.rs index 9230a01e9..01eb331e6 100644 --- a/runtime/src/precompiles/subnet.rs +++ b/runtime/src/precompiles/subnet.rs @@ -27,10 +27,10 @@ impl SubnetPrecompile { .map_or_else(vec::Vec::new, |slice| slice.to_vec()); // Avoiding borrowing conflicts match method_id { - id if id == get_method_id("registerNetwork(bytes,bytes,bytes)") => { + id if id == get_method_id("registerNetwork(bytes32,bytes,bytes,bytes)") => { Self::register_network(handle, &method_input) } - id if id == get_method_id("registerNetwork()") => { + id if id == get_method_id("registerNetwork(bytes32)") => { Self::register_network(handle, &[0_u8; 0]) } _ => Err(PrecompileFailure::Error { @@ -41,13 +41,15 @@ impl SubnetPrecompile { fn register_network(handle: &mut impl PrecompileHandle, data: &[u8]) -> PrecompileResult { let call = if data.is_empty() { + let hotkey = Self::parse_pub_key(data)?.into(); RuntimeCall::SubtensorModule( pallet_subtensor::Call::<Runtime>::register_network_with_identity { + hotkey, identity: None, }, ) } else { - let (subnet_name, github_repo, subnet_contact) = + let (pubkey, subnet_name, github_repo, subnet_contact) = Self::parse_register_network_parameters(data)?; let identity: pallet_subtensor::SubnetIdentityOf = pallet_subtensor::SubnetIdentityOf { @@ -59,6 +61,7 @@ impl SubnetPrecompile { // Create the register_network callcle RuntimeCall::SubtensorModule( pallet_subtensor::Call::<Runtime>::register_network_with_identity { + hotkey: pubkey.into(), identity: Some(identity), }, ) @@ -68,19 +71,33 @@ impl SubnetPrecompile { dispatch(handle, call, STAKING_CONTRACT_ADDRESS) } + fn parse_pub_key(data: &[u8]) -> Result<[u8; 32], PrecompileFailure> { + if data.len() < 32 { + return Err(PrecompileFailure::Error { + exit_status: ExitError::InvalidRange, + }); + } + let mut pubkey = [0u8; 32]; + pubkey.copy_from_slice(get_slice(data, 0, 32)?); + Ok(pubkey) + } + fn parse_register_network_parameters( data: &[u8], - ) -> Result<(vec::Vec<u8>, vec::Vec<u8>, vec::Vec<u8>), PrecompileFailure> { + ) -> Result<([u8; 32], vec::Vec<u8>, vec::Vec<u8>, vec::Vec<u8>), PrecompileFailure> { + let mut pubkey = [0u8; 32]; + pubkey.copy_from_slice(get_slice(data, 0, 32)?); + let mut buf = [0_u8; 4]; // get all start point for three data items: name, repo and contact - buf.copy_from_slice(get_slice(data, 28, 32)?); + buf.copy_from_slice(get_slice(data, 60, 64)?); let subnet_name_start: usize = u32::from_be_bytes(buf) as usize; - buf.copy_from_slice(get_slice(data, 60, 64)?); + buf.copy_from_slice(get_slice(data, 92, 96)?); let github_repo_start: usize = u32::from_be_bytes(buf) as usize; - buf.copy_from_slice(get_slice(data, 92, 96)?); + buf.copy_from_slice(get_slice(data, 124, 128)?); let subnet_contact_start: usize = u32::from_be_bytes(buf) as usize; // get name @@ -128,6 +145,6 @@ impl SubnetPrecompile { subnet_contact_start + subnet_contact_len + 32, )?); - Ok((name_vec, repo_vec, contact_vec)) + Ok((pubkey, name_vec, repo_vec, contact_vec)) } } From 5040e52262f9c20e496c20d391ee53c7eb94b2dc Mon Sep 17 00:00:00 2001 From: open-junius <zhou@opentensor.dev> Date: Thu, 16 Jan 2025 21:09:33 +0800 Subject: [PATCH 011/145] update sol --- runtime/src/precompiles/solidity/subnet.abi | 25 +++++++++++++++------ runtime/src/precompiles/solidity/subnet.sol | 15 ++++++++----- 2 files changed, 28 insertions(+), 12 deletions(-) diff --git a/runtime/src/precompiles/solidity/subnet.abi b/runtime/src/precompiles/solidity/subnet.abi index a89cf91f1..a11879241 100644 --- a/runtime/src/precompiles/solidity/subnet.abi +++ b/runtime/src/precompiles/solidity/subnet.abi @@ -1,6 +1,24 @@ [ { "inputs": [ + { + "internalType": "bytes32", + "name": "hotkey", + "type": "bytes32" + } + ], + "name": "registerNetwork", + "outputs": [], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "hotkey", + "type": "bytes32" + }, { "internalType": "bytes", "name": "subnetName", @@ -21,12 +39,5 @@ "outputs": [], "stateMutability": "payable", "type": "function" - }, - { - "inputs": [], - "name": "registerNetwork", - "outputs": [], - "stateMutability": "payable", - "type": "function" } ] \ No newline at end of file diff --git a/runtime/src/precompiles/solidity/subnet.sol b/runtime/src/precompiles/solidity/subnet.sol index 581788b03..c3d0a4a13 100644 --- a/runtime/src/precompiles/solidity/subnet.sol +++ b/runtime/src/precompiles/solidity/subnet.sol @@ -3,8 +3,13 @@ pragma solidity ^0.8.0; address constant ISTAKING_ADDRESS = 0x0000000000000000000000000000000000000803; interface ISubnet { - /// Registers a new network without specifying details. - function registerNetwork() external payable; - /// Registers a new network with specified subnet name, GitHub repository, and contact information. - function registerNetwork(bytes memory subnetName, bytes memory githubRepo, bytes memory subnetContact) external payable; -} \ No newline at end of file + /// Registers a new network without specifying details. + function registerNetwork(bytes32 hotkey) external payable; + /// Registers a new network with specified subnet name, GitHub repository, and contact information. + function registerNetwork( + bytes32 hotkey, + bytes memory subnetName, + bytes memory githubRepo, + bytes memory subnetContact + ) external payable; +} From 1341e7ac336119b36eea34b0a47bc7cd5544ce59 Mon Sep 17 00:00:00 2001 From: open-junius <zhou@opentensor.dev> Date: Fri, 17 Jan 2025 22:40:33 +0800 Subject: [PATCH 012/145] rename contract --- runtime/src/precompiles/mod.rs | 8 ++++---- runtime/src/precompiles/{subnets.rs => neuron.rs} | 12 ++++++------ .../precompiles/solidity/{subnets.abi => neuron.abi} | 0 .../precompiles/solidity/{subnets.sol => neuron.sol} | 4 ++-- 4 files changed, 12 insertions(+), 12 deletions(-) rename runtime/src/precompiles/{subnets.rs => neuron.rs} (83%) rename runtime/src/precompiles/solidity/{subnets.abi => neuron.abi} (100%) rename runtime/src/precompiles/solidity/{subnets.sol => neuron.sol} (82%) diff --git a/runtime/src/precompiles/mod.rs b/runtime/src/precompiles/mod.rs index c1524e19f..3d2c19ae9 100644 --- a/runtime/src/precompiles/mod.rs +++ b/runtime/src/precompiles/mod.rs @@ -26,16 +26,16 @@ use crate::{Runtime, RuntimeCall}; mod balance_transfer; mod ed25519; mod metagraph; +mod neuron; mod staking; mod subnet; -mod subnets; use balance_transfer::*; use ed25519::*; use metagraph::*; +use neuron::*; use staking::*; use subnet::*; -use subnets::*; pub struct FrontierPrecompiles<R>(PhantomData<R>); impl<R> Default for FrontierPrecompiles<R> where @@ -67,7 +67,7 @@ where hash(STAKING_PRECOMPILE_INDEX), hash(SUBNET_PRECOMPILE_INDEX), hash(METAGRAPH_PRECOMPILE_INDEX), - hash(SUBNETS_PRECOMPILE_INDEX), + hash(NEURON_PRECOMPILE_INDEX), ] } } @@ -96,7 +96,7 @@ where a if a == hash(METAGRAPH_PRECOMPILE_INDEX) => { Some(MetagraphPrecompile::execute(handle)) } - a if a == hash(SUBNETS_PRECOMPILE_INDEX) => Some(SubnetsPrecompile::execute(handle)), + a if a == hash(NEURON_PRECOMPILE_INDEX) => Some(NeuronPrecompile::execute(handle)), _ => None, } diff --git a/runtime/src/precompiles/subnets.rs b/runtime/src/precompiles/neuron.rs similarity index 83% rename from runtime/src/precompiles/subnets.rs rename to runtime/src/precompiles/neuron.rs index 19be425d5..341683cb3 100644 --- a/runtime/src/precompiles/subnets.rs +++ b/runtime/src/precompiles/neuron.rs @@ -4,14 +4,14 @@ use crate::precompiles::{dispatch, get_method_id, get_slice}; use sp_std::vec; use crate::{Runtime, RuntimeCall}; -pub const SUBNETS_PRECOMPILE_INDEX: u64 = 2052; +pub const NEURON_PRECOMPILE_INDEX: u64 = 2052; -// this is subnets smart contract's(0x0000000000000000000000000000000000000804) sr25519 address -pub const SUBNETS_CONTRACT_ADDRESS: &str = "5GKZiUUgTnWSz3BgiVBMehEKkLszsG4ZXnvgWpWFUFKqrqyn"; +// this is neuron smart contract's(0x0000000000000000000000000000000000000804) sr25519 address +pub const NEURON_CONTRACT_ADDRESS: &str = "5GKZiUUgTnWSz3BgiVBMehEKkLszsG4ZXnvgWpWFUFKqrqyn"; -pub struct SubnetsPrecompile; +pub struct NeuronPrecompile; -impl SubnetsPrecompile { +impl NeuronPrecompile { pub fn execute(handle: &mut impl PrecompileHandle) -> PrecompileResult { let txdata = handle.input(); let method_id = get_slice(txdata, 0, 4)?; @@ -37,7 +37,7 @@ impl SubnetsPrecompile { netuid, hotkey: hotkey.into(), }); - dispatch(handle, call, SUBNETS_CONTRACT_ADDRESS) + dispatch(handle, call, NEURON_CONTRACT_ADDRESS) } fn parse_netuid_hotkey_parameter(data: &[u8]) -> Result<(u16, [u8; 32]), PrecompileFailure> { diff --git a/runtime/src/precompiles/solidity/subnets.abi b/runtime/src/precompiles/solidity/neuron.abi similarity index 100% rename from runtime/src/precompiles/solidity/subnets.abi rename to runtime/src/precompiles/solidity/neuron.abi diff --git a/runtime/src/precompiles/solidity/subnets.sol b/runtime/src/precompiles/solidity/neuron.sol similarity index 82% rename from runtime/src/precompiles/solidity/subnets.sol rename to runtime/src/precompiles/solidity/neuron.sol index 5cbf72779..a76842339 100644 --- a/runtime/src/precompiles/solidity/subnets.sol +++ b/runtime/src/precompiles/solidity/neuron.sol @@ -1,8 +1,8 @@ pragma solidity ^0.8.0; -address constant ISUBNETS_ADDRESS = 0x0000000000000000000000000000000000000804; +address constant INeuron_ADDRESS = 0x0000000000000000000000000000000000000804; -interface ISubnets { +interface INeuron { /** * @dev Registers a neuron by calling `do_burned_registration` internally with the origin set to the ss58 mirror of the H160 address. * This allows the H160 to further call neuron-related methods and receive emissions. From 9637986afaffeae6579f78522eea41311da21289 Mon Sep 17 00:00:00 2001 From: Aliaksandr Tsurko <ales@opentensor.dev> Date: Fri, 17 Jan 2025 17:49:38 +0100 Subject: [PATCH 013/145] Fix try-runtime accounting invariants check --- pallets/subtensor/src/subnets/subnet.rs | 4 ++- pallets/subtensor/src/utils/try_state.rs | 34 ++++++++++++++-------- scripts/try-runtime-upgrade.sh | 37 ++++++++++++++++++++++++ 3 files changed, 62 insertions(+), 13 deletions(-) create mode 100755 scripts/try-runtime-upgrade.sh diff --git a/pallets/subtensor/src/subnets/subnet.rs b/pallets/subtensor/src/subnets/subnet.rs index 7b7de6aeb..5554a477b 100644 --- a/pallets/subtensor/src/subnets/subnet.rs +++ b/pallets/subtensor/src/subnets/subnet.rs @@ -2,6 +2,8 @@ use super::*; use frame_support::IterableStorageMap; use sp_core::Get; +pub(crate) const POOL_INITIAL_TAO: u64 = 100_000_000_000; + impl<T: Config> Pallet<T> { /// Retrieves the unique identifier (UID) for the root network. /// @@ -234,7 +236,7 @@ impl<T: Config> Pallet<T> { ); // Set subnet token symbol. // Put 100 TAO from lock into subnet TAO and produce numerically equal amount of Alpha - let mut pool_initial_tao = 100_000_000_000; + let mut pool_initial_tao = POOL_INITIAL_TAO; if pool_initial_tao > actual_tao_lock_amount { pool_initial_tao = actual_tao_lock_amount; } diff --git a/pallets/subtensor/src/utils/try_state.rs b/pallets/subtensor/src/utils/try_state.rs index 385a21bdd..62c4aabd6 100644 --- a/pallets/subtensor/src/utils/try_state.rs +++ b/pallets/subtensor/src/utils/try_state.rs @@ -1,4 +1,6 @@ use super::*; +#[cfg(feature = "try-runtime")] +use super::subnets::subnet::POOL_INITIAL_TAO; impl<T: Config> Pallet<T> { /// Checks if the accounting invariants for [`TotalStake`], [`TotalSubnetLocked`], and [`TotalIssuance`] are correct. @@ -16,10 +18,16 @@ impl<T: Config> Pallet<T> { use frame_support::traits::fungible::Inspect; // Calculate the total staked amount - let mut total_staked: u64 = 0; - for (_hotkey, _coldkey, stake) in Stake::<T>::iter() { - total_staked = total_staked.saturating_add(stake); - } + let total_staked = SubnetTAO::<T>::iter().fold(0u64, |acc, (netuid, stake)| { + let acc = acc.saturating_add(stake); + + if netuid == Self::get_root_netuid() { + // root network doesn't have initial pool TAO + acc + } else { + acc.saturating_sub(POOL_INITIAL_TAO) + } + }); // Verify that the calculated total stake matches the stored TotalStake ensure!( @@ -28,13 +36,13 @@ impl<T: Config> Pallet<T> { ); // Get the total subnet locked amount - let total_subnet_locked: u64 = Self::get_total_subnet_locked(); + let total_subnet_locked = Self::get_total_subnet_locked(); // Get the total currency issuance - let currency_issuance: u64 = T::Currency::total_issuance(); + let currency_issuance = T::Currency::total_issuance(); // Calculate the expected total issuance - let expected_total_issuance: u64 = currency_issuance + let expected_total_issuance = currency_issuance .saturating_add(total_staked) .saturating_add(total_subnet_locked); @@ -42,15 +50,17 @@ impl<T: Config> Pallet<T> { // // These values can be off slightly due to float rounding errors. // They are corrected every runtime upgrade. - const DELTA: u64 = 1000; - let diff = if TotalIssuance::<T>::get() > expected_total_issuance { - TotalIssuance::<T>::get().checked_sub(expected_total_issuance) + let delta = 1000; + let total_issuance = TotalIssuance::<T>::get(); + + let diff = if total_issuance > expected_total_issuance { + total_issuance.checked_sub(expected_total_issuance) } else { - expected_total_issuance.checked_sub(TotalIssuance::<T>::get()) + expected_total_issuance.checked_sub(total_issuance) } .expect("LHS > RHS"); ensure!( - diff <= DELTA, + diff <= delta, "TotalIssuance diff greater than allowable delta", ); diff --git a/scripts/try-runtime-upgrade.sh b/scripts/try-runtime-upgrade.sh new file mode 100755 index 000000000..7c3e5fe77 --- /dev/null +++ b/scripts/try-runtime-upgrade.sh @@ -0,0 +1,37 @@ +#!/usr/bin/env bash + +# Tries runtime upgrade (via try-runtime). +# +# Usage: +# try-runtime-upgrade.sh [-p <runtime-path>] [-u <live-chain-url>] +# +# Dependencies: +# - rust toolchain +# - try-runtime-cli + +set -eou pipefail + +runtime_wasm_path="./target/release/wbuild/node-subtensor-runtime/node_subtensor_runtime.compact.wasm" +live_chain_url="wss://dev.chain.opentensor.ai:443" + +parse_args() { + while getopts "p:u:" opt; do + case "${opt}" in + p) runtime_wasm_path="${OPTARG}" ;; + u) live_chain_url="${OPTARG}" ;; + *) echo "Usage: $(basename "$0") [-p <runtime-path>] [-u <live-chain-url>]" && exit 1 ;; + esac + done +} + +build_runtime() { + cargo build -p node-subtensor-runtime --release --features "metadata-hash,try-runtime" +} + +do_try_runtime() { + try-runtime --runtime "$runtime_wasm_path" on-runtime-upgrade live --uri "$live_chain_url" +} + +parse_args "$@" +build_runtime +do_try_runtime From 9df0d130664c986f844cbfff195ce8ace492188f Mon Sep 17 00:00:00 2001 From: Aliaksandr Tsurko <ales@opentensor.dev> Date: Fri, 17 Jan 2025 18:11:24 +0100 Subject: [PATCH 014/145] Reformat --- pallets/subtensor/src/utils/try_state.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pallets/subtensor/src/utils/try_state.rs b/pallets/subtensor/src/utils/try_state.rs index 62c4aabd6..46a3ba5e4 100644 --- a/pallets/subtensor/src/utils/try_state.rs +++ b/pallets/subtensor/src/utils/try_state.rs @@ -1,6 +1,6 @@ -use super::*; #[cfg(feature = "try-runtime")] use super::subnets::subnet::POOL_INITIAL_TAO; +use super::*; impl<T: Config> Pallet<T> { /// Checks if the accounting invariants for [`TotalStake`], [`TotalSubnetLocked`], and [`TotalIssuance`] are correct. From 5ff711dd9a83d2760f64bff2059e9c847d565794 Mon Sep 17 00:00:00 2001 From: Sam Johnson <sam@durosoft.com> Date: Fri, 17 Jan 2025 15:12:18 -0500 Subject: [PATCH 015/145] make docker image builds trigger on key branches --- .github/workflows/docker.yml | 31 ++++++++++++++++++++++++------- 1 file changed, 24 insertions(+), 7 deletions(-) diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml index fd00e9a31..4ace8c6fe 100644 --- a/.github/workflows/docker.yml +++ b/.github/workflows/docker.yml @@ -4,6 +4,11 @@ on: release: types: [published] workflow_dispatch: + push: + branches: + - devnet-ready + - devnet + - testnet permissions: contents: read @@ -32,11 +37,24 @@ jobs: username: ${{ github.actor }} password: ${{ secrets.GITHUB_TOKEN }} - - name: Extract metadata (tags, labels) for Docker - id: meta - uses: docker/metadata-action@v4 - with: - images: ghcr.io/${{ github.repository }} + - name: Determine Docker tag + id: tag + run: | + case "${{ github.ref_name }}" in + devnet-ready) + echo "tag=devnet-ready" >> $GITHUB_ENV + ;; + devnet) + echo "tag=devnet" >> $GITHUB_ENV + ;; + testnet) + echo "tag=testnet" >> $GITHUB_ENV + ;; + *) + echo "Branch not recognized for custom tagging." + exit 1 + ;; + esac - name: Build and push Docker image uses: docker/build-push-action@v4 @@ -44,6 +62,5 @@ jobs: context: . push: true tags: | - ${{ steps.meta.outputs.tags }} + ghcr.io/${{ github.repository }}:${{ env.tag }} ghcr.io/${{ github.repository }}:latest - labels: ${{ steps.meta.outputs.labels }} From 18a4c6de7e264427ce75ed36201fc122186bdbc8 Mon Sep 17 00:00:00 2001 From: Sam Johnson <sam@durosoft.com> Date: Fri, 17 Jan 2025 15:58:10 -0500 Subject: [PATCH 016/145] fix --- .github/workflows/docker.yml | 25 ++++++++++--------------- 1 file changed, 10 insertions(+), 15 deletions(-) diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml index 4ace8c6fe..fa15d8174 100644 --- a/.github/workflows/docker.yml +++ b/.github/workflows/docker.yml @@ -4,6 +4,12 @@ on: release: types: [published] workflow_dispatch: + inputs: + branch-or-tag: + description: "Branch or tag to use for the Docker image tag (optional)" + required: false + default: "" + push: branches: - devnet-ready @@ -40,21 +46,10 @@ jobs: - name: Determine Docker tag id: tag run: | - case "${{ github.ref_name }}" in - devnet-ready) - echo "tag=devnet-ready" >> $GITHUB_ENV - ;; - devnet) - echo "tag=devnet" >> $GITHUB_ENV - ;; - testnet) - echo "tag=testnet" >> $GITHUB_ENV - ;; - *) - echo "Branch not recognized for custom tagging." - exit 1 - ;; - esac + # Use the provided branch-or-tag input or fallback to the current ref + branch_or_tag="${{ github.event.inputs.branch-or-tag || github.ref_name }}" + echo "Determined branch or tag: $branch_or_tag" + echo "tag=$branch_or_tag" >> $GITHUB_ENV - name: Build and push Docker image uses: docker/build-push-action@v4 From 53cdd8f2ed2bea4d45a20724eca7ff2faaacd543 Mon Sep 17 00:00:00 2001 From: Sam Johnson <sam@durosoft.com> Date: Fri, 17 Jan 2025 16:00:42 -0500 Subject: [PATCH 017/145] use specified ref --- .github/workflows/docker.yml | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml index fa15d8174..85fd20269 100644 --- a/.github/workflows/docker.yml +++ b/.github/workflows/docker.yml @@ -6,7 +6,7 @@ on: workflow_dispatch: inputs: branch-or-tag: - description: "Branch or tag to use for the Docker image tag (optional)" + description: "Branch or tag to use for the Docker image tag and ref to checkout (optional)" required: false default: "" @@ -27,8 +27,19 @@ jobs: runs-on: SubtensorCI steps: + - name: Determine Docker tag and ref + id: tag + run: | + # Use the provided branch-or-tag input or fallback to the current ref + branch_or_tag="${{ github.event.inputs.branch-or-tag || github.ref_name }}" + echo "Determined branch or tag: $branch_or_tag" + echo "tag=$branch_or_tag" >> $GITHUB_ENV + echo "ref=$branch_or_tag" >> $GITHUB_ENV + - name: Checkout code uses: actions/checkout@v4 + with: + ref: ${{ env.ref }} - name: Set up QEMU uses: docker/setup-qemu-action@v2 @@ -43,14 +54,6 @@ jobs: username: ${{ github.actor }} password: ${{ secrets.GITHUB_TOKEN }} - - name: Determine Docker tag - id: tag - run: | - # Use the provided branch-or-tag input or fallback to the current ref - branch_or_tag="${{ github.event.inputs.branch-or-tag || github.ref_name }}" - echo "Determined branch or tag: $branch_or_tag" - echo "tag=$branch_or_tag" >> $GITHUB_ENV - - name: Build and push Docker image uses: docker/build-push-action@v4 with: From 92a476e478803309a883d827d6318fed62101859 Mon Sep 17 00:00:00 2001 From: Aliaksandr Tsurko <ales@opentensor.dev> Date: Fri, 17 Jan 2025 22:14:15 +0100 Subject: [PATCH 018/145] Change subnet initial supply fallback value --- pallets/subtensor/src/subnets/subnet.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/pallets/subtensor/src/subnets/subnet.rs b/pallets/subtensor/src/subnets/subnet.rs index 5554a477b..050514c4c 100644 --- a/pallets/subtensor/src/subnets/subnet.rs +++ b/pallets/subtensor/src/subnets/subnet.rs @@ -3,6 +3,7 @@ use frame_support::IterableStorageMap; use sp_core::Get; pub(crate) const POOL_INITIAL_TAO: u64 = 100_000_000_000; +pub(crate) const POOL_INITIAL_TAO_FALLBACK: u64 = 500; impl<T: Config> Pallet<T> { /// Retrieves the unique identifier (UID) for the root network. @@ -240,8 +241,8 @@ impl<T: Config> Pallet<T> { if pool_initial_tao > actual_tao_lock_amount { pool_initial_tao = actual_tao_lock_amount; } - if pool_initial_tao < 1 { - pool_initial_tao = 1; + if pool_initial_tao == 0 { + pool_initial_tao = POOL_INITIAL_TAO_FALLBACK; } let actual_tao_lock_amount_less_pool_tao = actual_tao_lock_amount.saturating_sub(pool_initial_tao); From fede19b80817315a512ec5a69b86db2198997e5c Mon Sep 17 00:00:00 2001 From: Sam Johnson <sam@durosoft.com> Date: Fri, 17 Jan 2025 16:17:26 -0500 Subject: [PATCH 019/145] only bump latest tag if this is a github release! --- .github/workflows/docker.yml | 2 -- 1 file changed, 2 deletions(-) diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml index 85fd20269..530c7cd8d 100644 --- a/.github/workflows/docker.yml +++ b/.github/workflows/docker.yml @@ -30,7 +30,6 @@ jobs: - name: Determine Docker tag and ref id: tag run: | - # Use the provided branch-or-tag input or fallback to the current ref branch_or_tag="${{ github.event.inputs.branch-or-tag || github.ref_name }}" echo "Determined branch or tag: $branch_or_tag" echo "tag=$branch_or_tag" >> $GITHUB_ENV @@ -61,4 +60,3 @@ jobs: push: true tags: | ghcr.io/${{ github.repository }}:${{ env.tag }} - ghcr.io/${{ github.repository }}:latest From 1f073d3a53e9d8e7cc2ae5d613c307b5f9439106 Mon Sep 17 00:00:00 2001 From: camfairchild <cameron@opentensor.dev> Date: Fri, 17 Jan 2025 19:51:55 -0500 Subject: [PATCH 020/145] calculate owner cut first --- .../subtensor/src/coinbase/run_coinbase.rs | 75 ++++++++++++------- 1 file changed, 48 insertions(+), 27 deletions(-) diff --git a/pallets/subtensor/src/coinbase/run_coinbase.rs b/pallets/subtensor/src/coinbase/run_coinbase.rs index 6d835cd40..a150df654 100644 --- a/pallets/subtensor/src/coinbase/run_coinbase.rs +++ b/pallets/subtensor/src/coinbase/run_coinbase.rs @@ -124,6 +124,9 @@ impl<T: Config> Pallet<T> { EmissionValues::<T>::insert(*netuid, tao_in); } + // == We'll save the owner cuts for each subnet. + let mut owner_cuts: BTreeMap<u16, u64> = BTreeMap::new(); + // --- 4. Distribute subnet emission into subnets based on mechanism type. for netuid in subnets.iter() { // Do not emit into root network. @@ -189,12 +192,27 @@ impl<T: Config> Pallet<T> { log::debug!("Increased TotalIssuance: {:?}", *total); }); + // Calculate the owner cut. + let owner_cut: u64 = I96F32::from_num(alpha_out_emission) + .saturating_mul(Self::get_float_subnet_owner_cut()) + .to_num::<u64>(); + log::debug!("Owner cut for netuid {:?}: {:?}", netuid, owner_cut); + // Store the owner cut for this subnet. + *owner_cuts.entry(*netuid).or_insert(0) = owner_cut; + + let remaining_emission: u64 = alpha_out_emission.saturating_sub(owner_cut); + log::debug!( + "Remaining emission for netuid {:?}: {:?}", + netuid, + remaining_emission + ); + // Get proportion of alpha out emission as root divs. let root_emission_in_alpha: I96F32 = - Self::get_root_divs_in_alpha(*netuid, I96F32::from_num(alpha_out_emission)); + Self::get_root_divs_in_alpha(*netuid, I96F32::from_num(remaining_emission)); // Subtract root divs from alpha divs. let pending_alpha_emission: I96F32 = - I96F32::from_num(alpha_out_emission).saturating_sub(root_emission_in_alpha); + I96F32::from_num(remaining_emission).saturating_sub(root_emission_in_alpha); // Sell root emission through the pool. let root_emission_in_tao: u64 = Self::swap_alpha_for_tao(*netuid, root_emission_in_alpha.to_num::<u64>()); @@ -225,12 +243,24 @@ impl<T: Config> Pallet<T> { BlocksSinceLastStep::<T>::insert(netuid, 0); LastMechansimStepBlock::<T>::insert(netuid, current_block); - // 5.2 Get and drain the subnet pending emission. + // 5.2.1 Get and drain the subnet pending emission. let pending_emission: u64 = PendingEmission::<T>::get(netuid); PendingEmission::<T>::insert(netuid, 0); - // Drain pending root divs and alpha emission. - Self::drain_pending_emission(netuid, pending_emission); + // 5.2.2 Get and drain the subnet pending root divs. + let pending_root_divs: u64 = PendingRootDivs::<T>::get(netuid); + PendingRootDivs::<T>::insert(netuid, 0); + + // 5.2.3 Get owner cut. + let owner_cut: u64 = *owner_cuts.get(&netuid).unwrap_or(&0); + + // 5.2.4 Drain pending root divs, alpha emission, and owner cut. + Self::drain_pending_emission( + netuid, + pending_emission, + pending_root_divs, + owner_cut, + ); } else { // Increment BlocksSinceLastStep::<T>::mutate(netuid, |total| *total = total.saturating_add(1)); @@ -238,31 +268,23 @@ impl<T: Config> Pallet<T> { } } - pub fn drain_pending_emission(netuid: u16, pending_emission: u64) { - let alpha_out: u64 = pending_emission; - - log::debug!( - "Draining pending emission for netuid {:?}: {:?}", - netuid, - alpha_out - ); - - // Calculate the 18% owner cut. - let owner_cut: u64 = I96F32::from_num(alpha_out) - .saturating_mul(Self::get_float_subnet_owner_cut()) - .to_num::<u64>(); - log::debug!("Owner cut for netuid {:?}: {:?}", netuid, owner_cut); - - let remaining_emission: u64 = alpha_out.saturating_sub(owner_cut); + pub fn drain_pending_emission( + netuid: u16, + pending_alpha_emission: u64, + pending_root_divs: u64, + owner_cut: u64, + ) { log::debug!( - "Remaining emission for netuid {:?}: {:?}", + "Draining pending alpha emission for netuid {:?}: {:?}, with pending root divs {:?}, and owner cut {:?}", netuid, - remaining_emission + pending_alpha_emission, + pending_root_divs, + owner_cut ); // Run the epoch() --> hotkey emission. let hotkey_emission: Vec<(T::AccountId, u64, u64)> = - Self::epoch(netuid, remaining_emission); + Self::epoch(netuid, pending_alpha_emission); log::debug!( "Hotkey emission for netuid {:?}: {:?}", netuid, @@ -463,14 +485,13 @@ impl<T: Config> Pallet<T> { // For all the root-alpha divs give this proportion of the swapped tao to the root participants. let _ = TaoDividendsPerSubnet::<T>::clear_prefix(netuid, u32::MAX, None); - let total_root_divs_to_distribute = PendingRootDivs::<T>::get(netuid); - PendingRootDivs::<T>::insert(netuid, 0); + for (hotkey_j, root_divs) in root_alpha_divs.iter() { let proportion: I96F32 = I96F32::from_num(*root_divs) .checked_div(I96F32::from_num(total_root_alpha_divs)) .unwrap_or(I96F32::from_num(0)); let root_divs_to_pay: u64 = proportion - .saturating_mul(I96F32::from_num(total_root_divs_to_distribute)) + .saturating_mul(I96F32::from_num(pending_root_divs)) .to_num::<u64>(); log::debug!( "Proportion for hotkey {:?}: {:?}, root_divs_to_pay: {:?}", From 23f9b87f37737fde2765899ebf7b4fdd28d496b2 Mon Sep 17 00:00:00 2001 From: open-junius <zhou@opentensor.dev> Date: Mon, 20 Jan 2025 10:05:24 +0800 Subject: [PATCH 021/145] fix e2e test --- runtime/src/precompiles/subnet.rs | 27 +++++++++++---------------- 1 file changed, 11 insertions(+), 16 deletions(-) diff --git a/runtime/src/precompiles/subnet.rs b/runtime/src/precompiles/subnet.rs index 01eb331e6..db3adfc1b 100644 --- a/runtime/src/precompiles/subnet.rs +++ b/runtime/src/precompiles/subnet.rs @@ -31,7 +31,7 @@ impl SubnetPrecompile { Self::register_network(handle, &method_input) } id if id == get_method_id("registerNetwork(bytes32)") => { - Self::register_network(handle, &[0_u8; 0]) + Self::register_network(handle, &method_input) } _ => Err(PrecompileFailure::Error { exit_status: ExitError::InvalidRange, @@ -40,15 +40,17 @@ impl SubnetPrecompile { } fn register_network(handle: &mut impl PrecompileHandle, data: &[u8]) -> PrecompileResult { - let call = if data.is_empty() { - let hotkey = Self::parse_pub_key(data)?.into(); + let call = if data.len() == 32 { + let mut hotkey = [0u8; 32]; + hotkey.copy_from_slice(get_slice(data, 0, 32)?); + RuntimeCall::SubtensorModule( pallet_subtensor::Call::<Runtime>::register_network_with_identity { - hotkey, + hotkey: hotkey.into(), identity: None, }, ) - } else { + } else if data.len() > 32 { let (pubkey, subnet_name, github_repo, subnet_contact) = Self::parse_register_network_parameters(data)?; @@ -65,23 +67,16 @@ impl SubnetPrecompile { identity: Some(identity), }, ) + } else { + return Err(PrecompileFailure::Error { + exit_status: ExitError::InvalidRange, + }); }; // Dispatch the register_network call dispatch(handle, call, STAKING_CONTRACT_ADDRESS) } - fn parse_pub_key(data: &[u8]) -> Result<[u8; 32], PrecompileFailure> { - if data.len() < 32 { - return Err(PrecompileFailure::Error { - exit_status: ExitError::InvalidRange, - }); - } - let mut pubkey = [0u8; 32]; - pubkey.copy_from_slice(get_slice(data, 0, 32)?); - Ok(pubkey) - } - fn parse_register_network_parameters( data: &[u8], ) -> Result<([u8; 32], vec::Vec<u8>, vec::Vec<u8>, vec::Vec<u8>), PrecompileFailure> { From 9de1927a0a866b3c768432972e315c6744c63a18 Mon Sep 17 00:00:00 2001 From: open-junius <zhou@opentensor.dev> Date: Mon, 20 Jan 2025 10:22:41 +0800 Subject: [PATCH 022/145] commit Cargo.lock --- runtime/src/precompiles/subnet.rs | 67 +++++++++++++++++-------------- 1 file changed, 36 insertions(+), 31 deletions(-) diff --git a/runtime/src/precompiles/subnet.rs b/runtime/src/precompiles/subnet.rs index db3adfc1b..f883a6996 100644 --- a/runtime/src/precompiles/subnet.rs +++ b/runtime/src/precompiles/subnet.rs @@ -40,37 +40,42 @@ impl SubnetPrecompile { } fn register_network(handle: &mut impl PrecompileHandle, data: &[u8]) -> PrecompileResult { - let call = if data.len() == 32 { - let mut hotkey = [0u8; 32]; - hotkey.copy_from_slice(get_slice(data, 0, 32)?); - - RuntimeCall::SubtensorModule( - pallet_subtensor::Call::<Runtime>::register_network_with_identity { - hotkey: hotkey.into(), - identity: None, - }, - ) - } else if data.len() > 32 { - let (pubkey, subnet_name, github_repo, subnet_contact) = - Self::parse_register_network_parameters(data)?; - - let identity: pallet_subtensor::SubnetIdentityOf = pallet_subtensor::SubnetIdentityOf { - subnet_name, - github_repo, - subnet_contact, - }; - - // Create the register_network callcle - RuntimeCall::SubtensorModule( - pallet_subtensor::Call::<Runtime>::register_network_with_identity { - hotkey: pubkey.into(), - identity: Some(identity), - }, - ) - } else { - return Err(PrecompileFailure::Error { - exit_status: ExitError::InvalidRange, - }); + let call = match data.len() { + 32 => { + let mut hotkey = [0u8; 32]; + hotkey.copy_from_slice(get_slice(data, 0, 32)?); + + RuntimeCall::SubtensorModule( + pallet_subtensor::Call::<Runtime>::register_network_with_identity { + hotkey: hotkey.into(), + identity: None, + }, + ) + } + 32.. => { + let (pubkey, subnet_name, github_repo, subnet_contact) = + Self::parse_register_network_parameters(data)?; + + let identity: pallet_subtensor::SubnetIdentityOf = + pallet_subtensor::SubnetIdentityOf { + subnet_name, + github_repo, + subnet_contact, + }; + + // Create the register_network callcle + RuntimeCall::SubtensorModule( + pallet_subtensor::Call::<Runtime>::register_network_with_identity { + hotkey: pubkey.into(), + identity: Some(identity), + }, + ) + } + _ => { + return Err(PrecompileFailure::Error { + exit_status: ExitError::InvalidRange, + }); + } }; // Dispatch the register_network call From 3d05f7bcf325970bba94ef55cad5ebfd59f7e6e4 Mon Sep 17 00:00:00 2001 From: open-junius <zhou@opentensor.dev> Date: Mon, 20 Jan 2025 10:23:09 +0800 Subject: [PATCH 023/145] cargo clippy --- runtime/src/precompiles/subnet.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/runtime/src/precompiles/subnet.rs b/runtime/src/precompiles/subnet.rs index f883a6996..c1827f77a 100644 --- a/runtime/src/precompiles/subnet.rs +++ b/runtime/src/precompiles/subnet.rs @@ -52,7 +52,7 @@ impl SubnetPrecompile { }, ) } - 32.. => { + 33.. => { let (pubkey, subnet_name, github_repo, subnet_contact) = Self::parse_register_network_parameters(data)?; From c4ea8a4a741e6ba9b2345cfc7232bf420d6098ea Mon Sep 17 00:00:00 2001 From: Sam Johnson <sam@durosoft.com> Date: Mon, 20 Jan 2025 10:45:13 -0500 Subject: [PATCH 024/145] commit Cargo.lock changes --- Cargo.lock | 184 +++++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 137 insertions(+), 47 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 964c583e1..90c2fb86f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1550,9 +1550,9 @@ dependencies = [ [[package]] name = "crossbeam-queue" -version = "0.3.12" +version = "0.3.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f58bbc28f91df819d0aa2a2c00cd19754769c2fad90579b3592b1c9ba7a3115" +checksum = "df0346b5d5e76ac2fe4e327c5fd1118d6be7c51dfb18f9b7922923f287471e35" dependencies = [ "crossbeam-utils", ] @@ -2187,9 +2187,9 @@ checksum = "c22d4b5885b6aa2fe5e8b9329fb8d232bf739e434e6b87347c63bdd00c120f60" dependencies = [ "crunchy", "fixed-hash", - "impl-codec", + "impl-codec 0.6.0", "impl-rlp", - "impl-serde", + "impl-serde 0.4.0", "scale-info", "tiny-keccak", ] @@ -2220,12 +2220,12 @@ checksum = "02d215cbf040552efcbe99a38372fe80ab9d00268e20012b79fcd0f073edd8ee" dependencies = [ "ethbloom", "fixed-hash", - "impl-codec", + "impl-codec 0.6.0", "impl-rlp", - "impl-serde", - "primitive-types", + "impl-serde 0.4.0", + "primitive-types 0.12.2", "scale-info", - "uint", + "uint 0.9.5", ] [[package]] @@ -2269,7 +2269,7 @@ dependencies = [ "evm-runtime", "log", "parity-scale-codec", - "primitive-types", + "primitive-types 0.12.2", "rlp", "scale-info", "serde", @@ -2283,7 +2283,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d1da6cedc5cedb4208e59467106db0d1f50db01b920920589f8e672c02fdc04f" dependencies = [ "parity-scale-codec", - "primitive-types", + "primitive-types 0.12.2", "scale-info", "serde", ] @@ -2297,7 +2297,7 @@ dependencies = [ "environmental", "evm-core", "evm-runtime", - "primitive-types", + "primitive-types 0.12.2", ] [[package]] @@ -2309,7 +2309,7 @@ dependencies = [ "auto_impl", "environmental", "evm-core", - "primitive-types", + "primitive-types 0.12.2", "sha3", ] @@ -2694,7 +2694,7 @@ version = "1.0.0-dev" source = "git+https://github.com/opentensor/frontier?rev=635bdac882#635bdac882333afed827053f31ef56ab739f7a2e" dependencies = [ "hex", - "impl-serde", + "impl-serde 0.4.0", "libsecp256k1", "log", "parity-scale-codec", @@ -3876,6 +3876,26 @@ dependencies = [ "parity-scale-codec", ] +[[package]] +name = "impl-codec" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b67aa010c1e3da95bf151bd8b4c059b2ed7e75387cdb969b4f8f2723a43f9941" +dependencies = [ + "parity-scale-codec", +] + +[[package]] +name = "impl-num-traits" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "803d15461ab0dcc56706adf266158acbc44ccf719bf7d0af30705f58b90a4b8c" +dependencies = [ + "integer-sqrt", + "num-traits", + "uint 0.10.0", +] + [[package]] name = "impl-rlp" version = "0.3.0" @@ -3894,6 +3914,15 @@ dependencies = [ "serde", ] +[[package]] +name = "impl-serde" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a143eada6a1ec4aefa5049037a26a6d597bfd64f8c026d07b77133e02b7dd0b" +dependencies = [ + "serde", +] + [[package]] name = "impl-trait-for-tuples" version = "0.2.2" @@ -4430,7 +4459,7 @@ dependencies = [ "sha2 0.10.8", "smallvec", "thiserror", - "uint", + "uint 0.9.5", "unsigned-varint 0.7.2", "void", ] @@ -4892,7 +4921,7 @@ dependencies = [ "tokio-util", "tracing", "trust-dns-resolver", - "uint", + "uint 0.9.5", "unsigned-varint 0.8.0", "url", "webpki", @@ -6635,7 +6664,7 @@ dependencies = [ "lru 0.8.1", "parity-util-mem-derive", "parking_lot 0.12.3", - "primitive-types", + "primitive-types 0.12.2", "smallvec", "winapi", ] @@ -6877,7 +6906,7 @@ dependencies = [ "libc", "log", "polkavm-assembler", - "polkavm-common", + "polkavm-common 0.9.0", "polkavm-linux-raw", ] @@ -6899,13 +6928,28 @@ dependencies = [ "log", ] +[[package]] +name = "polkavm-common" +version = "0.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f0dbafef4ab6ceecb4982ac3b550df430ef4f9fdbf07c108b7d4f91a0682fce" + [[package]] name = "polkavm-derive" version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ae8c4bea6f3e11cd89bb18bcdddac10bd9a24015399bd1c485ad68a985a19606" dependencies = [ - "polkavm-derive-impl-macro", + "polkavm-derive-impl-macro 0.9.0", +] + +[[package]] +name = "polkavm-derive" +version = "0.17.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "206caf322dfc02144510ad8360ff2051e5072f0874dcab3b410f78cdd52d0ebb" +dependencies = [ + "polkavm-derive-impl-macro 0.17.0", ] [[package]] @@ -6914,7 +6958,19 @@ version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5c4fdfc49717fb9a196e74a5d28e0bc764eb394a2c803eb11133a31ac996c60c" dependencies = [ - "polkavm-common", + "polkavm-common 0.9.0", + "proc-macro2", + "quote", + "syn 2.0.90", +] + +[[package]] +name = "polkavm-derive-impl" +version = "0.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42565aed4adbc4034612d0b17dea8db3681fb1bd1aed040d6edc5455a9f478a1" +dependencies = [ + "polkavm-common 0.17.0", "proc-macro2", "quote", "syn 2.0.90", @@ -6926,7 +6982,17 @@ version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8ba81f7b5faac81e528eb6158a6f3c9e0bb1008e0ffa19653bc8dea925ecb429" dependencies = [ - "polkavm-derive-impl", + "polkavm-derive-impl 0.9.0", + "syn 2.0.90", +] + +[[package]] +name = "polkavm-derive-impl-macro" +version = "0.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "86d9838e95241b0bce4fe269cdd4af96464160505840ed5a8ac8536119ba19e2" +dependencies = [ + "polkavm-derive-impl 0.17.0", "syn 2.0.90", ] @@ -6940,7 +7006,7 @@ dependencies = [ "hashbrown 0.14.5", "log", "object 0.32.2", - "polkavm-common", + "polkavm-common 0.9.0", "regalloc2 0.9.3", "rustc-demangle", ] @@ -7077,11 +7143,23 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0b34d9fd68ae0b74a41b21c03c2f62847aa0ffea044eee893b4c140b37e244e2" dependencies = [ "fixed-hash", - "impl-codec", + "impl-codec 0.6.0", "impl-rlp", - "impl-serde", + "impl-serde 0.4.0", "scale-info", - "uint", + "uint 0.9.5", +] + +[[package]] +name = "primitive-types" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d15600a7d856470b7d278b3fe0e311fe28c2526348549f8ef2ff7db3299c87f5" +dependencies = [ + "fixed-hash", + "impl-codec 0.7.0", + "impl-num-traits", + "uint 0.10.0", ] [[package]] @@ -9327,9 +9405,9 @@ checksum = "cd0b0ec5f1c1ca621c432a25813d8d60c88abe6d3e08a3eb9cf37d97a0fe3d73" [[package]] name = "serde" -version = "1.0.216" +version = "1.0.215" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b9781016e935a97e8beecf0c933758c97a5520d32930e460142b4cd80c6338e" +checksum = "6513c1ad0b11a9376da888e3e0baa0077f1aed55c17f50e7b2397136129fb88f" dependencies = [ "serde_derive", ] @@ -9364,9 +9442,9 @@ dependencies = [ [[package]] name = "serde_derive" -version = "1.0.216" +version = "1.0.215" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "46f859dbbf73865c6627ed570e78961cd3ac92407a2d117204c49232485da55e" +checksum = "ad1e866f866923f252f05c889987993144fb74e722403468a4ebd70c3cd756c0" dependencies = [ "proc-macro2", "quote", @@ -9858,7 +9936,7 @@ dependencies = [ "futures", "hash-db", "hash256-std-hasher", - "impl-serde", + "impl-serde 0.4.0", "itertools 0.11.0", "k256", "libsecp256k1", @@ -9868,7 +9946,7 @@ dependencies = [ "parity-scale-codec", "parking_lot 0.12.3", "paste", - "primitive-types", + "primitive-types 0.12.2", "rand", "scale-info", "schnorrkel", @@ -9892,7 +9970,7 @@ dependencies = [ [[package]] name = "sp-crypto-ec-utils" version = "0.10.0" -source = "git+https://github.com/paritytech/polkadot-sdk#8614dc0e055d06de4a3774ac1da0a422b33f34e2" +source = "git+https://github.com/paritytech/polkadot-sdk#44766de645c741fd03d159fca7f17cd6e99c33e3" dependencies = [ "ark-bls12-377", "ark-bls12-377-ext", @@ -9988,7 +10066,7 @@ dependencies = [ [[package]] name = "sp-debug-derive" version = "14.0.0" -source = "git+https://github.com/paritytech/polkadot-sdk#8614dc0e055d06de4a3774ac1da0a422b33f34e2" +source = "git+https://github.com/paritytech/polkadot-sdk#44766de645c741fd03d159fca7f17cd6e99c33e3" dependencies = [ "proc-macro2", "quote", @@ -9998,7 +10076,7 @@ dependencies = [ [[package]] name = "sp-externalities" version = "0.25.0" -source = "git+https://github.com/paritytech/polkadot-sdk#8614dc0e055d06de4a3774ac1da0a422b33f34e2" +source = "git+https://github.com/paritytech/polkadot-sdk#44766de645c741fd03d159fca7f17cd6e99c33e3" dependencies = [ "environmental", "parity-scale-codec", @@ -10051,7 +10129,7 @@ dependencies = [ "libsecp256k1", "log", "parity-scale-codec", - "polkavm-derive", + "polkavm-derive 0.9.1", "rustversion", "secp256k1", "sp-core", @@ -10176,13 +10254,13 @@ dependencies = [ [[package]] name = "sp-runtime-interface" version = "24.0.0" -source = "git+https://github.com/paritytech/polkadot-sdk#8614dc0e055d06de4a3774ac1da0a422b33f34e2" +source = "git+https://github.com/paritytech/polkadot-sdk#44766de645c741fd03d159fca7f17cd6e99c33e3" dependencies = [ "bytes", "impl-trait-for-tuples", "parity-scale-codec", - "polkavm-derive", - "primitive-types", + "polkavm-derive 0.17.1", + "primitive-types 0.13.1", "sp-externalities 0.25.0", "sp-runtime-interface-proc-macro 17.0.0", "sp-std 14.0.0 (git+https://github.com/paritytech/polkadot-sdk)", @@ -10200,8 +10278,8 @@ dependencies = [ "bytes", "impl-trait-for-tuples", "parity-scale-codec", - "polkavm-derive", - "primitive-types", + "polkavm-derive 0.9.1", + "primitive-types 0.12.2", "sp-externalities 0.29.0", "sp-runtime-interface-proc-macro 18.0.0", "sp-std 14.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-stable2409)", @@ -10214,7 +10292,7 @@ dependencies = [ [[package]] name = "sp-runtime-interface-proc-macro" version = "17.0.0" -source = "git+https://github.com/paritytech/polkadot-sdk#8614dc0e055d06de4a3774ac1da0a422b33f34e2" +source = "git+https://github.com/paritytech/polkadot-sdk#44766de645c741fd03d159fca7f17cd6e99c33e3" dependencies = [ "Inflector", "expander", @@ -10316,14 +10394,14 @@ source = "git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-stable [[package]] name = "sp-std" version = "14.0.0" -source = "git+https://github.com/paritytech/polkadot-sdk#8614dc0e055d06de4a3774ac1da0a422b33f34e2" +source = "git+https://github.com/paritytech/polkadot-sdk#44766de645c741fd03d159fca7f17cd6e99c33e3" [[package]] name = "sp-storage" version = "19.0.0" -source = "git+https://github.com/paritytech/polkadot-sdk#8614dc0e055d06de4a3774ac1da0a422b33f34e2" +source = "git+https://github.com/paritytech/polkadot-sdk#44766de645c741fd03d159fca7f17cd6e99c33e3" dependencies = [ - "impl-serde", + "impl-serde 0.5.0", "parity-scale-codec", "ref-cast", "serde", @@ -10335,7 +10413,7 @@ name = "sp-storage" version = "21.0.0" source = "git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-stable2409#87971b3e92721bdf10bf40b410eaae779d494ca0" dependencies = [ - "impl-serde", + "impl-serde 0.4.0", "parity-scale-codec", "ref-cast", "serde", @@ -10357,7 +10435,7 @@ dependencies = [ [[package]] name = "sp-tracing" version = "16.0.0" -source = "git+https://github.com/paritytech/polkadot-sdk#8614dc0e055d06de4a3774ac1da0a422b33f34e2" +source = "git+https://github.com/paritytech/polkadot-sdk#44766de645c741fd03d159fca7f17cd6e99c33e3" dependencies = [ "parity-scale-codec", "tracing", @@ -10427,7 +10505,7 @@ name = "sp-version" version = "37.0.0" source = "git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-stable2409#87971b3e92721bdf10bf40b410eaae779d494ca0" dependencies = [ - "impl-serde", + "impl-serde 0.4.0", "parity-scale-codec", "parity-wasm", "scale-info", @@ -10453,7 +10531,7 @@ dependencies = [ [[package]] name = "sp-wasm-interface" version = "20.0.0" -source = "git+https://github.com/paritytech/polkadot-sdk#8614dc0e055d06de4a3774ac1da0a422b33f34e2" +source = "git+https://github.com/paritytech/polkadot-sdk#44766de645c741fd03d159fca7f17cd6e99c33e3" dependencies = [ "anyhow", "impl-trait-for-tuples", @@ -11627,6 +11705,18 @@ dependencies = [ "static_assertions", ] +[[package]] +name = "uint" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "909988d098b2f738727b161a106cfc7cab00c539c2687a8836f8e565976fb53e" +dependencies = [ + "byteorder", + "crunchy", + "hex", + "static_assertions", +] + [[package]] name = "unicode-bidi" version = "0.3.17" From 8aa57171345bcabbbb88e03df344b96c24f0016d Mon Sep 17 00:00:00 2001 From: camfairchild <cameron@opentensor.dev> Date: Mon, 20 Jan 2025 12:02:21 -0500 Subject: [PATCH 025/145] put owner cut in a pending --- Cargo.lock | 184 +++++------------- .../subtensor/src/coinbase/run_coinbase.rs | 9 +- pallets/subtensor/src/lib.rs | 3 + 3 files changed, 57 insertions(+), 139 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 90c2fb86f..964c583e1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1550,9 +1550,9 @@ dependencies = [ [[package]] name = "crossbeam-queue" -version = "0.3.11" +version = "0.3.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df0346b5d5e76ac2fe4e327c5fd1118d6be7c51dfb18f9b7922923f287471e35" +checksum = "0f58bbc28f91df819d0aa2a2c00cd19754769c2fad90579b3592b1c9ba7a3115" dependencies = [ "crossbeam-utils", ] @@ -2187,9 +2187,9 @@ checksum = "c22d4b5885b6aa2fe5e8b9329fb8d232bf739e434e6b87347c63bdd00c120f60" dependencies = [ "crunchy", "fixed-hash", - "impl-codec 0.6.0", + "impl-codec", "impl-rlp", - "impl-serde 0.4.0", + "impl-serde", "scale-info", "tiny-keccak", ] @@ -2220,12 +2220,12 @@ checksum = "02d215cbf040552efcbe99a38372fe80ab9d00268e20012b79fcd0f073edd8ee" dependencies = [ "ethbloom", "fixed-hash", - "impl-codec 0.6.0", + "impl-codec", "impl-rlp", - "impl-serde 0.4.0", - "primitive-types 0.12.2", + "impl-serde", + "primitive-types", "scale-info", - "uint 0.9.5", + "uint", ] [[package]] @@ -2269,7 +2269,7 @@ dependencies = [ "evm-runtime", "log", "parity-scale-codec", - "primitive-types 0.12.2", + "primitive-types", "rlp", "scale-info", "serde", @@ -2283,7 +2283,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d1da6cedc5cedb4208e59467106db0d1f50db01b920920589f8e672c02fdc04f" dependencies = [ "parity-scale-codec", - "primitive-types 0.12.2", + "primitive-types", "scale-info", "serde", ] @@ -2297,7 +2297,7 @@ dependencies = [ "environmental", "evm-core", "evm-runtime", - "primitive-types 0.12.2", + "primitive-types", ] [[package]] @@ -2309,7 +2309,7 @@ dependencies = [ "auto_impl", "environmental", "evm-core", - "primitive-types 0.12.2", + "primitive-types", "sha3", ] @@ -2694,7 +2694,7 @@ version = "1.0.0-dev" source = "git+https://github.com/opentensor/frontier?rev=635bdac882#635bdac882333afed827053f31ef56ab739f7a2e" dependencies = [ "hex", - "impl-serde 0.4.0", + "impl-serde", "libsecp256k1", "log", "parity-scale-codec", @@ -3876,26 +3876,6 @@ dependencies = [ "parity-scale-codec", ] -[[package]] -name = "impl-codec" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b67aa010c1e3da95bf151bd8b4c059b2ed7e75387cdb969b4f8f2723a43f9941" -dependencies = [ - "parity-scale-codec", -] - -[[package]] -name = "impl-num-traits" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "803d15461ab0dcc56706adf266158acbc44ccf719bf7d0af30705f58b90a4b8c" -dependencies = [ - "integer-sqrt", - "num-traits", - "uint 0.10.0", -] - [[package]] name = "impl-rlp" version = "0.3.0" @@ -3914,15 +3894,6 @@ dependencies = [ "serde", ] -[[package]] -name = "impl-serde" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4a143eada6a1ec4aefa5049037a26a6d597bfd64f8c026d07b77133e02b7dd0b" -dependencies = [ - "serde", -] - [[package]] name = "impl-trait-for-tuples" version = "0.2.2" @@ -4459,7 +4430,7 @@ dependencies = [ "sha2 0.10.8", "smallvec", "thiserror", - "uint 0.9.5", + "uint", "unsigned-varint 0.7.2", "void", ] @@ -4921,7 +4892,7 @@ dependencies = [ "tokio-util", "tracing", "trust-dns-resolver", - "uint 0.9.5", + "uint", "unsigned-varint 0.8.0", "url", "webpki", @@ -6664,7 +6635,7 @@ dependencies = [ "lru 0.8.1", "parity-util-mem-derive", "parking_lot 0.12.3", - "primitive-types 0.12.2", + "primitive-types", "smallvec", "winapi", ] @@ -6906,7 +6877,7 @@ dependencies = [ "libc", "log", "polkavm-assembler", - "polkavm-common 0.9.0", + "polkavm-common", "polkavm-linux-raw", ] @@ -6928,28 +6899,13 @@ dependencies = [ "log", ] -[[package]] -name = "polkavm-common" -version = "0.17.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f0dbafef4ab6ceecb4982ac3b550df430ef4f9fdbf07c108b7d4f91a0682fce" - [[package]] name = "polkavm-derive" version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ae8c4bea6f3e11cd89bb18bcdddac10bd9a24015399bd1c485ad68a985a19606" dependencies = [ - "polkavm-derive-impl-macro 0.9.0", -] - -[[package]] -name = "polkavm-derive" -version = "0.17.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "206caf322dfc02144510ad8360ff2051e5072f0874dcab3b410f78cdd52d0ebb" -dependencies = [ - "polkavm-derive-impl-macro 0.17.0", + "polkavm-derive-impl-macro", ] [[package]] @@ -6958,19 +6914,7 @@ version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5c4fdfc49717fb9a196e74a5d28e0bc764eb394a2c803eb11133a31ac996c60c" dependencies = [ - "polkavm-common 0.9.0", - "proc-macro2", - "quote", - "syn 2.0.90", -] - -[[package]] -name = "polkavm-derive-impl" -version = "0.17.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42565aed4adbc4034612d0b17dea8db3681fb1bd1aed040d6edc5455a9f478a1" -dependencies = [ - "polkavm-common 0.17.0", + "polkavm-common", "proc-macro2", "quote", "syn 2.0.90", @@ -6982,17 +6926,7 @@ version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8ba81f7b5faac81e528eb6158a6f3c9e0bb1008e0ffa19653bc8dea925ecb429" dependencies = [ - "polkavm-derive-impl 0.9.0", - "syn 2.0.90", -] - -[[package]] -name = "polkavm-derive-impl-macro" -version = "0.17.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "86d9838e95241b0bce4fe269cdd4af96464160505840ed5a8ac8536119ba19e2" -dependencies = [ - "polkavm-derive-impl 0.17.0", + "polkavm-derive-impl", "syn 2.0.90", ] @@ -7006,7 +6940,7 @@ dependencies = [ "hashbrown 0.14.5", "log", "object 0.32.2", - "polkavm-common 0.9.0", + "polkavm-common", "regalloc2 0.9.3", "rustc-demangle", ] @@ -7143,23 +7077,11 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0b34d9fd68ae0b74a41b21c03c2f62847aa0ffea044eee893b4c140b37e244e2" dependencies = [ "fixed-hash", - "impl-codec 0.6.0", + "impl-codec", "impl-rlp", - "impl-serde 0.4.0", + "impl-serde", "scale-info", - "uint 0.9.5", -] - -[[package]] -name = "primitive-types" -version = "0.13.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d15600a7d856470b7d278b3fe0e311fe28c2526348549f8ef2ff7db3299c87f5" -dependencies = [ - "fixed-hash", - "impl-codec 0.7.0", - "impl-num-traits", - "uint 0.10.0", + "uint", ] [[package]] @@ -9405,9 +9327,9 @@ checksum = "cd0b0ec5f1c1ca621c432a25813d8d60c88abe6d3e08a3eb9cf37d97a0fe3d73" [[package]] name = "serde" -version = "1.0.215" +version = "1.0.216" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6513c1ad0b11a9376da888e3e0baa0077f1aed55c17f50e7b2397136129fb88f" +checksum = "0b9781016e935a97e8beecf0c933758c97a5520d32930e460142b4cd80c6338e" dependencies = [ "serde_derive", ] @@ -9442,9 +9364,9 @@ dependencies = [ [[package]] name = "serde_derive" -version = "1.0.215" +version = "1.0.216" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad1e866f866923f252f05c889987993144fb74e722403468a4ebd70c3cd756c0" +checksum = "46f859dbbf73865c6627ed570e78961cd3ac92407a2d117204c49232485da55e" dependencies = [ "proc-macro2", "quote", @@ -9936,7 +9858,7 @@ dependencies = [ "futures", "hash-db", "hash256-std-hasher", - "impl-serde 0.4.0", + "impl-serde", "itertools 0.11.0", "k256", "libsecp256k1", @@ -9946,7 +9868,7 @@ dependencies = [ "parity-scale-codec", "parking_lot 0.12.3", "paste", - "primitive-types 0.12.2", + "primitive-types", "rand", "scale-info", "schnorrkel", @@ -9970,7 +9892,7 @@ dependencies = [ [[package]] name = "sp-crypto-ec-utils" version = "0.10.0" -source = "git+https://github.com/paritytech/polkadot-sdk#44766de645c741fd03d159fca7f17cd6e99c33e3" +source = "git+https://github.com/paritytech/polkadot-sdk#8614dc0e055d06de4a3774ac1da0a422b33f34e2" dependencies = [ "ark-bls12-377", "ark-bls12-377-ext", @@ -10066,7 +9988,7 @@ dependencies = [ [[package]] name = "sp-debug-derive" version = "14.0.0" -source = "git+https://github.com/paritytech/polkadot-sdk#44766de645c741fd03d159fca7f17cd6e99c33e3" +source = "git+https://github.com/paritytech/polkadot-sdk#8614dc0e055d06de4a3774ac1da0a422b33f34e2" dependencies = [ "proc-macro2", "quote", @@ -10076,7 +9998,7 @@ dependencies = [ [[package]] name = "sp-externalities" version = "0.25.0" -source = "git+https://github.com/paritytech/polkadot-sdk#44766de645c741fd03d159fca7f17cd6e99c33e3" +source = "git+https://github.com/paritytech/polkadot-sdk#8614dc0e055d06de4a3774ac1da0a422b33f34e2" dependencies = [ "environmental", "parity-scale-codec", @@ -10129,7 +10051,7 @@ dependencies = [ "libsecp256k1", "log", "parity-scale-codec", - "polkavm-derive 0.9.1", + "polkavm-derive", "rustversion", "secp256k1", "sp-core", @@ -10254,13 +10176,13 @@ dependencies = [ [[package]] name = "sp-runtime-interface" version = "24.0.0" -source = "git+https://github.com/paritytech/polkadot-sdk#44766de645c741fd03d159fca7f17cd6e99c33e3" +source = "git+https://github.com/paritytech/polkadot-sdk#8614dc0e055d06de4a3774ac1da0a422b33f34e2" dependencies = [ "bytes", "impl-trait-for-tuples", "parity-scale-codec", - "polkavm-derive 0.17.1", - "primitive-types 0.13.1", + "polkavm-derive", + "primitive-types", "sp-externalities 0.25.0", "sp-runtime-interface-proc-macro 17.0.0", "sp-std 14.0.0 (git+https://github.com/paritytech/polkadot-sdk)", @@ -10278,8 +10200,8 @@ dependencies = [ "bytes", "impl-trait-for-tuples", "parity-scale-codec", - "polkavm-derive 0.9.1", - "primitive-types 0.12.2", + "polkavm-derive", + "primitive-types", "sp-externalities 0.29.0", "sp-runtime-interface-proc-macro 18.0.0", "sp-std 14.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-stable2409)", @@ -10292,7 +10214,7 @@ dependencies = [ [[package]] name = "sp-runtime-interface-proc-macro" version = "17.0.0" -source = "git+https://github.com/paritytech/polkadot-sdk#44766de645c741fd03d159fca7f17cd6e99c33e3" +source = "git+https://github.com/paritytech/polkadot-sdk#8614dc0e055d06de4a3774ac1da0a422b33f34e2" dependencies = [ "Inflector", "expander", @@ -10394,14 +10316,14 @@ source = "git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-stable [[package]] name = "sp-std" version = "14.0.0" -source = "git+https://github.com/paritytech/polkadot-sdk#44766de645c741fd03d159fca7f17cd6e99c33e3" +source = "git+https://github.com/paritytech/polkadot-sdk#8614dc0e055d06de4a3774ac1da0a422b33f34e2" [[package]] name = "sp-storage" version = "19.0.0" -source = "git+https://github.com/paritytech/polkadot-sdk#44766de645c741fd03d159fca7f17cd6e99c33e3" +source = "git+https://github.com/paritytech/polkadot-sdk#8614dc0e055d06de4a3774ac1da0a422b33f34e2" dependencies = [ - "impl-serde 0.5.0", + "impl-serde", "parity-scale-codec", "ref-cast", "serde", @@ -10413,7 +10335,7 @@ name = "sp-storage" version = "21.0.0" source = "git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-stable2409#87971b3e92721bdf10bf40b410eaae779d494ca0" dependencies = [ - "impl-serde 0.4.0", + "impl-serde", "parity-scale-codec", "ref-cast", "serde", @@ -10435,7 +10357,7 @@ dependencies = [ [[package]] name = "sp-tracing" version = "16.0.0" -source = "git+https://github.com/paritytech/polkadot-sdk#44766de645c741fd03d159fca7f17cd6e99c33e3" +source = "git+https://github.com/paritytech/polkadot-sdk#8614dc0e055d06de4a3774ac1da0a422b33f34e2" dependencies = [ "parity-scale-codec", "tracing", @@ -10505,7 +10427,7 @@ name = "sp-version" version = "37.0.0" source = "git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-stable2409#87971b3e92721bdf10bf40b410eaae779d494ca0" dependencies = [ - "impl-serde 0.4.0", + "impl-serde", "parity-scale-codec", "parity-wasm", "scale-info", @@ -10531,7 +10453,7 @@ dependencies = [ [[package]] name = "sp-wasm-interface" version = "20.0.0" -source = "git+https://github.com/paritytech/polkadot-sdk#44766de645c741fd03d159fca7f17cd6e99c33e3" +source = "git+https://github.com/paritytech/polkadot-sdk#8614dc0e055d06de4a3774ac1da0a422b33f34e2" dependencies = [ "anyhow", "impl-trait-for-tuples", @@ -11705,18 +11627,6 @@ dependencies = [ "static_assertions", ] -[[package]] -name = "uint" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "909988d098b2f738727b161a106cfc7cab00c539c2687a8836f8e565976fb53e" -dependencies = [ - "byteorder", - "crunchy", - "hex", - "static_assertions", -] - [[package]] name = "unicode-bidi" version = "0.3.17" diff --git a/pallets/subtensor/src/coinbase/run_coinbase.rs b/pallets/subtensor/src/coinbase/run_coinbase.rs index a150df654..11ea4887f 100644 --- a/pallets/subtensor/src/coinbase/run_coinbase.rs +++ b/pallets/subtensor/src/coinbase/run_coinbase.rs @@ -225,6 +225,10 @@ impl<T: Config> Pallet<T> { PendingEmission::<T>::mutate(*netuid, |total| { *total = total.saturating_add(pending_alpha_emission.to_num::<u64>()); }); + // Accumulate the owner cut in pending. + PendingOwnerCut::<T>::mutate(*netuid, |total| { + *total = total.saturating_add(owner_cut); + }); } // --- 5. Drain pending emission through the subnet based on tempo. @@ -251,8 +255,9 @@ impl<T: Config> Pallet<T> { let pending_root_divs: u64 = PendingRootDivs::<T>::get(netuid); PendingRootDivs::<T>::insert(netuid, 0); - // 5.2.3 Get owner cut. - let owner_cut: u64 = *owner_cuts.get(&netuid).unwrap_or(&0); + // 5.2.3 Get owner cut and drain. + let owner_cut: u64 = PendingOwnerCut::<T>::get(netuid); + PendingOwnerCut::<T>::insert(netuid, 0); // 5.2.4 Drain pending root divs, alpha emission, and owner cut. Self::drain_pending_emission( diff --git a/pallets/subtensor/src/lib.rs b/pallets/subtensor/src/lib.rs index 7f75568ba..4a2150f7f 100644 --- a/pallets/subtensor/src/lib.rs +++ b/pallets/subtensor/src/lib.rs @@ -1097,6 +1097,9 @@ pub mod pallet { /// --- MAP ( netuid ) --> pending_root_emission pub type PendingRootDivs<T> = StorageMap<_, Identity, u16, u64, ValueQuery, DefaultZeroU64<T>>; #[pallet::storage] + /// --- MAP ( netuid ) --> pending_owner_cut + pub type PendingOwnerCut<T> = StorageMap<_, Identity, u16, u64, ValueQuery, DefaultZeroU64<T>>; + #[pallet::storage] /// --- MAP ( netuid ) --> blocks_since_last_step pub type BlocksSinceLastStep<T> = StorageMap<_, Identity, u16, u64, ValueQuery, DefaultBlocksSinceLastStep<T>>; From eef3ce637a1888286b00ef8386a7faa55d8d6edb Mon Sep 17 00:00:00 2001 From: camfairchild <cameron@opentensor.dev> Date: Mon, 20 Jan 2025 14:49:25 -0500 Subject: [PATCH 026/145] move val prop to outside func --- pallets/subtensor/src/coinbase/run_coinbase.rs | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/pallets/subtensor/src/coinbase/run_coinbase.rs b/pallets/subtensor/src/coinbase/run_coinbase.rs index 11ea4887f..259bf68cd 100644 --- a/pallets/subtensor/src/coinbase/run_coinbase.rs +++ b/pallets/subtensor/src/coinbase/run_coinbase.rs @@ -18,7 +18,11 @@ pub struct WeightsTlockPayload { } impl<T: Config> Pallet<T> { - pub fn get_root_divs_in_alpha(netuid: u16, alpha_out_emission: I96F32) -> I96F32 { + pub fn get_root_divs_in_alpha( + netuid: u16, + alpha_out_emission: I96F32, + validator_proportion: I96F32, + ) -> I96F32 { // Get total TAO on root. let total_root_tao: I96F32 = I96F32::from_num(SubnetTAO::<T>::get(0)); // Get total ALPHA on subnet. @@ -32,7 +36,8 @@ impl<T: Config> Pallet<T> { // Get root proportion of alpha_out dividends. let root_divs_in_alpha: I96F32 = root_proportion .saturating_mul(alpha_out_emission) - .saturating_mul(I96F32::from_num(0.41)); + .saturating_mul(validator_proportion); // % of emission that goes to *all* validators. + // Return root_divs_in_alpha } @@ -207,9 +212,14 @@ impl<T: Config> Pallet<T> { remaining_emission ); + // Validators get 50% of remaining emission. + let validator_proportion: I96F32 = I96F32::from_num(0.5); // Get proportion of alpha out emission as root divs. - let root_emission_in_alpha: I96F32 = - Self::get_root_divs_in_alpha(*netuid, I96F32::from_num(remaining_emission)); + let root_emission_in_alpha: I96F32 = Self::get_root_divs_in_alpha( + *netuid, + I96F32::from_num(remaining_emission), + validator_proportion, + ); // Subtract root divs from alpha divs. let pending_alpha_emission: I96F32 = I96F32::from_num(remaining_emission).saturating_sub(root_emission_in_alpha); From 2bb5e208455e37ac91ec393ca3f98955d659409a Mon Sep 17 00:00:00 2001 From: Cameron Fairchild <cameron@opentensor.ai> Date: Tue, 21 Jan 2025 14:57:13 -0500 Subject: [PATCH 027/145] Fix/charge-for-swap-before-schedule (#1141) * charge for swap before sched * add swap cost to the events * pass swap cost into swap_coldkey call * fmt * oops, add arg * commit Cargo.lock * bump spec * fix tests --- pallets/subtensor/src/benchmarks.rs | 2 +- pallets/subtensor/src/macros/dispatches.rs | 12 +++- pallets/subtensor/src/macros/events.rs | 4 ++ pallets/subtensor/src/swap/swap_coldkey.rs | 5 +- pallets/subtensor/src/tests/swap_coldkey.rs | 67 ++++++++++++++++++--- runtime/src/lib.rs | 2 +- 6 files changed, 78 insertions(+), 14 deletions(-) diff --git a/pallets/subtensor/src/benchmarks.rs b/pallets/subtensor/src/benchmarks.rs index ecb5a1303..30d1f39e1 100644 --- a/pallets/subtensor/src/benchmarks.rs +++ b/pallets/subtensor/src/benchmarks.rs @@ -515,7 +515,7 @@ reveal_weights { Identities::<T>::insert(&old_coldkey, identity); // Benchmark setup complete, now execute the extrinsic -}: swap_coldkey(RawOrigin::Root, old_coldkey.clone(), new_coldkey.clone()) +}: swap_coldkey(RawOrigin::Root, old_coldkey.clone(), new_coldkey.clone(), swap_cost) batch_reveal_weights { let tempo: u16 = 0; diff --git a/pallets/subtensor/src/macros/dispatches.rs b/pallets/subtensor/src/macros/dispatches.rs index 7073a1413..285c9df66 100644 --- a/pallets/subtensor/src/macros/dispatches.rs +++ b/pallets/subtensor/src/macros/dispatches.rs @@ -962,12 +962,13 @@ mod dispatches { origin: OriginFor<T>, old_coldkey: T::AccountId, new_coldkey: T::AccountId, + swap_cost: u64, ) -> DispatchResultWithPostInfo { // Ensure it's called with root privileges (scheduler has root privileges) ensure_root(origin)?; log::info!("swap_coldkey: {:?} -> {:?}", old_coldkey, new_coldkey); - Self::do_swap_coldkey(&old_coldkey, &new_coldkey) + Self::do_swap_coldkey(&old_coldkey, &new_coldkey, swap_cost) } /// Sets the childkey take for a given hotkey. @@ -1327,6 +1328,13 @@ mod dispatches { Error::<T>::SwapAlreadyScheduled ); + // Calculate the swap cost and ensure sufficient balance + let swap_cost = Self::get_key_swap_cost(); + ensure!( + Self::can_remove_balance_from_coldkey_account(&who, swap_cost), + Error::<T>::NotEnoughBalanceToPaySwapColdKey + ); + let current_block: BlockNumberFor<T> = <frame_system::Pallet<T>>::block_number(); let duration: BlockNumberFor<T> = ColdkeySwapScheduleDuration::<T>::get(); let when: BlockNumberFor<T> = current_block.saturating_add(duration); @@ -1334,6 +1342,7 @@ mod dispatches { let call = Call::<T>::swap_coldkey { old_coldkey: who.clone(), new_coldkey: new_coldkey.clone(), + swap_cost, }; let bound_call = T::Preimages::bound(LocalCallOf::<T>::from(call.clone())) @@ -1354,6 +1363,7 @@ mod dispatches { old_coldkey: who.clone(), new_coldkey: new_coldkey.clone(), execution_block: when, + swap_cost, }); Ok(().into()) diff --git a/pallets/subtensor/src/macros/events.rs b/pallets/subtensor/src/macros/events.rs index 30566ac47..c1d25b8f4 100644 --- a/pallets/subtensor/src/macros/events.rs +++ b/pallets/subtensor/src/macros/events.rs @@ -155,6 +155,8 @@ mod events { old_coldkey: T::AccountId, /// the account ID of new coldkey new_coldkey: T::AccountId, + /// the swap cost + swap_cost: u64, }, /// All balance of a hotkey has been unstaked and transferred to a new coldkey AllBalanceUnstakedAndTransferredToNewColdkey { @@ -175,6 +177,8 @@ mod events { new_coldkey: T::AccountId, /// The arbitration block for the coldkey swap execution_block: BlockNumberFor<T>, + /// The swap cost + swap_cost: u64, }, /// The arbitration period has been extended ArbitrationPeriodExtended { diff --git a/pallets/subtensor/src/swap/swap_coldkey.rs b/pallets/subtensor/src/swap/swap_coldkey.rs index 2f0a6f2c6..075538421 100644 --- a/pallets/subtensor/src/swap/swap_coldkey.rs +++ b/pallets/subtensor/src/swap/swap_coldkey.rs @@ -33,6 +33,7 @@ impl<T: Config> Pallet<T> { pub fn do_swap_coldkey( old_coldkey: &T::AccountId, new_coldkey: &T::AccountId, + swap_cost: u64, ) -> DispatchResultWithPostInfo { // 2. Initialize the weight for this operation let mut weight: Weight = T::DbWeight::get().reads(2); @@ -55,8 +56,7 @@ impl<T: Config> Pallet<T> { Identities::<T>::insert(new_coldkey, identity); } - // 6. Calculate the swap cost and ensure sufficient balance - let swap_cost = Self::get_key_swap_cost(); + // 6. Ensure sufficient balance for the swap cost ensure!( Self::can_remove_balance_from_coldkey_account(old_coldkey, swap_cost), Error::<T>::NotEnoughBalanceToPaySwapColdKey @@ -83,6 +83,7 @@ impl<T: Config> Pallet<T> { Self::deposit_event(Event::ColdkeySwapped { old_coldkey: old_coldkey.clone(), new_coldkey: new_coldkey.clone(), + swap_cost, }); // 12. Return the result with the updated weight diff --git a/pallets/subtensor/src/tests/swap_coldkey.rs b/pallets/subtensor/src/tests/swap_coldkey.rs index 0cbcecfaf..bf4f1ad40 100644 --- a/pallets/subtensor/src/tests/swap_coldkey.rs +++ b/pallets/subtensor/src/tests/swap_coldkey.rs @@ -709,7 +709,8 @@ fn test_do_swap_coldkey_success() { assert_ok!(SubtensorModule::do_swap_coldkey( // <<Test as Config>::RuntimeOrigin>::signed(old_coldkey), &old_coldkey, - &new_coldkey + &new_coldkey, + swap_cost )); // Log state after swap @@ -782,6 +783,7 @@ fn test_do_swap_coldkey_success() { Event::ColdkeySwapped { old_coldkey, new_coldkey, + swap_cost, } .into(), ); @@ -1195,7 +1197,11 @@ fn test_do_swap_coldkey_with_subnet_ownership() { OwnedHotkeys::<Test>::insert(old_coldkey, vec![hotkey]); // Perform the swap - assert_ok!(SubtensorModule::do_swap_coldkey(&old_coldkey, &new_coldkey)); + assert_ok!(SubtensorModule::do_swap_coldkey( + &old_coldkey, + &new_coldkey, + swap_cost + )); // Verify subnet ownership transfer assert_eq!(SubnetOwner::<Test>::get(netuid), new_coldkey); @@ -1652,8 +1658,10 @@ fn test_schedule_swap_coldkey_success() { let old_coldkey: U256 = U256::from(1); let new_coldkey: U256 = U256::from(2); + let swap_cost = SubtensorModule::get_key_swap_cost(); + // Add balance to the old coldkey account - SubtensorModule::add_balance_to_coldkey_account(&old_coldkey, 1000); + SubtensorModule::add_balance_to_coldkey_account(&old_coldkey, swap_cost + 1_000); // Schedule the coldkey swap assert_ok!(SubtensorModule::schedule_swap_coldkey( @@ -1673,6 +1681,7 @@ fn test_schedule_swap_coldkey_success() { old_coldkey, new_coldkey, execution_block: expected_execution_block, + swap_cost, } .into(), ); @@ -1689,7 +1698,9 @@ fn test_schedule_swap_coldkey_duplicate() { let old_coldkey = U256::from(1); let new_coldkey = U256::from(2); - SubtensorModule::add_balance_to_coldkey_account(&old_coldkey, 2000); + let swap_cost = SubtensorModule::get_key_swap_cost(); + + SubtensorModule::add_balance_to_coldkey_account(&old_coldkey, swap_cost + 2_000); assert_ok!(SubtensorModule::schedule_swap_coldkey( <<Test as Config>::RuntimeOrigin>::signed(old_coldkey), @@ -1734,6 +1745,8 @@ fn test_schedule_swap_coldkey_execution() { "Initial ownership check failed" ); + let swap_cost = SubtensorModule::get_key_swap_cost(); + // Schedule the swap assert_ok!(SubtensorModule::schedule_swap_coldkey( <<Test as Config>::RuntimeOrigin>::signed(old_coldkey), @@ -1749,6 +1762,7 @@ fn test_schedule_swap_coldkey_execution() { old_coldkey, new_coldkey, execution_block, + swap_cost, } .into(), ); @@ -1790,6 +1804,7 @@ fn test_schedule_swap_coldkey_execution() { Event::ColdkeySwapped { old_coldkey, new_coldkey, + swap_cost, } .into(), ); @@ -1807,7 +1822,8 @@ fn test_direct_swap_coldkey_call_fails() { SubtensorModule::swap_coldkey( <<Test as Config>::RuntimeOrigin>::signed(old_coldkey), old_coldkey, - new_coldkey + new_coldkey, + 0 ), BadOrigin ); @@ -1822,7 +1838,9 @@ fn test_schedule_swap_coldkey_with_pending_swap() { let new_coldkey1 = U256::from(2); let new_coldkey2 = U256::from(3); - SubtensorModule::add_balance_to_coldkey_account(&old_coldkey, 2000); + let swap_cost = SubtensorModule::get_key_swap_cost(); + + SubtensorModule::add_balance_to_coldkey_account(&old_coldkey, swap_cost + 1_000); assert_ok!(SubtensorModule::schedule_swap_coldkey( <<Test as Config>::RuntimeOrigin>::signed(old_coldkey), @@ -1876,7 +1894,11 @@ fn test_coldkey_swap_delegate_identity_updated() { assert!(Identities::<Test>::get(old_coldkey).is_some()); assert!(Identities::<Test>::get(new_coldkey).is_none()); - assert_ok!(SubtensorModule::do_swap_coldkey(&old_coldkey, &new_coldkey)); + assert_ok!(SubtensorModule::do_swap_coldkey( + &old_coldkey, + &new_coldkey, + burn_cost + )); assert!(Identities::<Test>::get(old_coldkey).is_none()); assert!(Identities::<Test>::get(new_coldkey).is_some()); @@ -1912,7 +1934,11 @@ fn test_coldkey_swap_no_identity_no_changes() { assert!(Identities::<Test>::get(old_coldkey).is_none()); // Perform the coldkey swap - assert_ok!(SubtensorModule::do_swap_coldkey(&old_coldkey, &new_coldkey)); + assert_ok!(SubtensorModule::do_swap_coldkey( + &old_coldkey, + &new_coldkey, + burn_cost + )); // Ensure no identities have been changed assert!(Identities::<Test>::get(old_coldkey).is_none()); @@ -1956,10 +1982,33 @@ fn test_coldkey_swap_no_identity_no_changes_newcoldkey_exists() { assert!(Identities::<Test>::get(old_coldkey).is_none()); // Perform the coldkey swap - assert_ok!(SubtensorModule::do_swap_coldkey(&old_coldkey, &new_coldkey)); + assert_ok!(SubtensorModule::do_swap_coldkey( + &old_coldkey, + &new_coldkey, + burn_cost + )); // Ensure no identities have been changed assert!(Identities::<Test>::get(old_coldkey).is_none()); assert!(Identities::<Test>::get(new_coldkey).is_some()); }); } + +// SKIP_WASM_BUILD=1 RUST_LOG=info cargo test --test swap_coldkey -- test_cant_schedule_swap_without_enough_to_burn --exact --nocapture +#[test] +fn test_cant_schedule_swap_without_enough_to_burn() { + new_test_ext(1).execute_with(|| { + let old_coldkey = U256::from(3); + let new_coldkey = U256::from(4); + let hotkey = U256::from(5); + + let burn_cost = SubtensorModule::get_key_swap_cost(); + assert_noop!( + SubtensorModule::schedule_swap_coldkey( + <<Test as Config>::RuntimeOrigin>::signed(old_coldkey), + new_coldkey + ), + Error::<Test>::NotEnoughBalanceToPaySwapColdKey + ); + }); +} diff --git a/runtime/src/lib.rs b/runtime/src/lib.rs index 72221b9ff..128d2b8f3 100644 --- a/runtime/src/lib.rs +++ b/runtime/src/lib.rs @@ -220,7 +220,7 @@ pub const VERSION: RuntimeVersion = RuntimeVersion { // `spec_version`, and `authoring_version` are the same between Wasm and native. // This value is set to 100 to notify Polkadot-JS App (https://polkadot.js.org/apps) to use // the compatible custom types. - spec_version: 222, + spec_version: 223, impl_version: 1, apis: RUNTIME_API_VERSIONS, transaction_version: 1, From 09e205b1bd151dfe50d4486528b2e4a5f6f0690b Mon Sep 17 00:00:00 2001 From: Cameron Fairchild <cameron@opentensor.ai> Date: Tue, 21 Jan 2025 15:28:51 -0500 Subject: [PATCH 028/145] Fix/per 1k estimate always zero (#1152) * add fix for return per 1k * extract helper and add test --- .../subtensor/src/rpc_info/delegate_info.rs | 38 +++++++++++++++---- pallets/subtensor/src/tests/delegate_info.rs | 33 ++++++++++++++++ pallets/subtensor/src/tests/mod.rs | 1 + 3 files changed, 64 insertions(+), 8 deletions(-) create mode 100644 pallets/subtensor/src/tests/delegate_info.rs diff --git a/pallets/subtensor/src/rpc_info/delegate_info.rs b/pallets/subtensor/src/rpc_info/delegate_info.rs index 5cd234b48..5776c48af 100644 --- a/pallets/subtensor/src/rpc_info/delegate_info.rs +++ b/pallets/subtensor/src/rpc_info/delegate_info.rs @@ -21,6 +21,34 @@ pub struct DelegateInfo<T: Config> { } impl<T: Config> Pallet<T> { + fn return_per_1000_tao( + take: Compact<u16>, + total_stake: U64F64, + emissions_per_day: U64F64, + ) -> U64F64 { + // Get the take as a percentage and subtract it from 1 for remainder. + let without_take: U64F64 = U64F64::from_num(1) + .saturating_sub(U64F64::from_num(take.0).saturating_div(u16::MAX.into())); + + if total_stake > U64F64::from_num(0) { + emissions_per_day + .saturating_mul(without_take) + // Divide by 1000 TAO for return per 1k + .saturating_div(total_stake.saturating_div(U64F64::from_num(1000.0 * 1e9))) + } else { + U64F64::from_num(0) + } + } + + #[cfg(test)] + pub fn return_per_1000_tao_test( + take: Compact<u16>, + total_stake: U64F64, + emissions_per_day: U64F64, + ) -> U64F64 { + Self::return_per_1000_tao(take, total_stake, emissions_per_day) + } + fn get_delegate_by_existing_account(delegate: AccountIdOf<T>) -> DelegateInfo<T> { let mut nominators = Vec::<(T::AccountId, Compact<u64>)>::new(); @@ -63,14 +91,8 @@ impl<T: Config> Pallet<T> { let total_stake: U64F64 = Self::get_stake_for_hotkey_on_subnet(&delegate.clone(), Self::get_root_netuid()).into(); - let return_per_1000: U64F64 = if total_stake > U64F64::from_num(0) { - emissions_per_day - .saturating_mul(u16::MAX.saturating_sub(take.0).into()) - .saturating_div(u16::MAX.into()) - .saturating_div(total_stake.saturating_div(U64F64::from_num(1000))) - } else { - U64F64::from_num(0) - }; + let return_per_1000: U64F64 = + Self::return_per_1000_tao(take, total_stake, emissions_per_day); DelegateInfo { delegate_ss58: delegate.clone(), diff --git a/pallets/subtensor/src/tests/delegate_info.rs b/pallets/subtensor/src/tests/delegate_info.rs new file mode 100644 index 000000000..78ea48482 --- /dev/null +++ b/pallets/subtensor/src/tests/delegate_info.rs @@ -0,0 +1,33 @@ +use codec::Compact; +use substrate_fixed::types::U64F64; + +use super::mock::*; + +#[test] +fn test_return_per_1000_tao() { + let take = // 18% take to the Validator + Compact::<u16>::from((U64F64::from_num(0.18 * u16::MAX as f64)).saturating_to_num::<u16>()); + + // 10_000 TAO total validator stake + let total_stake = U64F64::from_num(10_000.0 * 1e9); + // 1000 TAO emissions per day + let emissions_per_day = U64F64::from_num(1000.0 * 1e9); + + let return_per_1000 = + SubtensorModule::return_per_1000_tao_test(take, total_stake, emissions_per_day); + + // We expect 82 TAO per day with 10% of total_stake + let expected_return_per_1000 = U64F64::from_num(82.0); + + let diff_from_expected: f64 = (return_per_1000 / U64F64::from_num(1e9)) + .saturating_sub(expected_return_per_1000) + .to_num::<f64>(); + + let eps: f64 = 0.0005e9; // Precision within 0.0005 TAO + assert!( + diff_from_expected.abs() <= eps, + "Difference from expected: {} is greater than precision: {}", + diff_from_expected, + eps + ); +} diff --git a/pallets/subtensor/src/tests/mod.rs b/pallets/subtensor/src/tests/mod.rs index e0fef9d55..6865c9fa4 100644 --- a/pallets/subtensor/src/tests/mod.rs +++ b/pallets/subtensor/src/tests/mod.rs @@ -1,6 +1,7 @@ mod batch_tx; mod children; mod coinbase; +mod delegate_info; mod difficulty; mod emission; mod epoch; From d392516865272997a982258e968b6dd8557d6c2c Mon Sep 17 00:00:00 2001 From: Greg Zaitsev <gregz@opentensor.dev> Date: Tue, 21 Jan 2025 15:32:28 -0500 Subject: [PATCH 029/145] Send staking and unstaking fees to SubnetTAO --- pallets/subtensor/src/staking/stake_utils.rs | 63 +++++++++---- pallets/subtensor/src/tests/staking.rs | 97 ++++++++++++++++++++ 2 files changed, 140 insertions(+), 20 deletions(-) diff --git a/pallets/subtensor/src/staking/stake_utils.rs b/pallets/subtensor/src/staking/stake_utils.rs index 524cd2069..b79b141d0 100644 --- a/pallets/subtensor/src/staking/stake_utils.rs +++ b/pallets/subtensor/src/staking/stake_utils.rs @@ -1,5 +1,7 @@ use super::*; +use crate::DefaultMinStake; use share_pool::{SharePool, SharePoolDataOperations}; +use sp_core::Get; use sp_std::ops::Neg; use substrate_fixed::types::{I64F64, I96F32, U64F64}; @@ -621,11 +623,19 @@ impl<T: Config> Pallet<T> { // }); // } - // Step 4. Deposit and log the unstaking event. + // Step 4. Reduce tao amount by staking fee and credit this fee to SubnetTAO + let fee = DefaultMinStake::<T>::get(); + let tao_unstaked = tao.saturating_sub(fee); + let actual_fee = tao.saturating_sub(tao_unstaked); + SubnetTAO::<T>::mutate(netuid, |total| { + *total = total.saturating_add(actual_fee); + }); + + // Step 5. Deposit and log the unstaking event. Self::deposit_event(Event::StakeRemoved( coldkey.clone(), hotkey.clone(), - tao, + tao_unstaked, alpha, netuid, )); @@ -633,13 +643,13 @@ impl<T: Config> Pallet<T> { "StakeRemoved( coldkey: {:?}, hotkey:{:?}, tao: {:?}, alpha:{:?}, netuid: {:?} )", coldkey.clone(), hotkey.clone(), - tao, + tao_unstaked, alpha, netuid ); - // Step 5: Return the amount of TAO unstaked. - tao + // Step 6: Return the amount of TAO unstaked. + tao_unstaked } /// Stakes TAO into a subnet for a given hotkey and coldkey pair. @@ -651,24 +661,37 @@ impl<T: Config> Pallet<T> { netuid: u16, tao: u64, ) -> u64 { - // Step 1. Swap the tao to alpha. - let alpha: u64 = Self::swap_tao_for_alpha(netuid, tao); - - // Step 2: Increase the alpha on the hotkey account. - Self::increase_stake_for_hotkey_and_coldkey_on_subnet(hotkey, coldkey, netuid, alpha); - - // Step 4: Update the list of hotkeys staking for this coldkey - let mut staking_hotkeys = StakingHotkeys::<T>::get(coldkey); - if !staking_hotkeys.contains(hotkey) { - staking_hotkeys.push(hotkey.clone()); - StakingHotkeys::<T>::insert(coldkey, staking_hotkeys.clone()); + // Step 1. Reduce tao amount by staking fee and credit this fee to SubnetTAO + // At this point tao was already withdrawn from the user balance and is considered + // available + let fee = DefaultMinStake::<T>::get(); + let tao_staked = tao.saturating_sub(fee); + let actual_fee = tao.saturating_sub(tao_staked); + + // Step 2. Swap the tao to alpha. + let alpha: u64 = Self::swap_tao_for_alpha(netuid, tao_staked); + if (tao_staked > 0) && (alpha > 0) { + // Step 3: Increase the alpha on the hotkey account. + Self::increase_stake_for_hotkey_and_coldkey_on_subnet(hotkey, coldkey, netuid, alpha); + + // Step 4: Update the list of hotkeys staking for this coldkey + let mut staking_hotkeys = StakingHotkeys::<T>::get(coldkey); + if !staking_hotkeys.contains(hotkey) { + staking_hotkeys.push(hotkey.clone()); + StakingHotkeys::<T>::insert(coldkey, staking_hotkeys.clone()); + } } - // Step 5. Deposit and log the staking event. + // Step 5. Increase Tao reserves by the fee amount. + SubnetTAO::<T>::mutate(netuid, |total| { + *total = total.saturating_add(actual_fee); + }); + + // Step 6. Deposit and log the staking event. Self::deposit_event(Event::StakeAdded( coldkey.clone(), hotkey.clone(), - tao, + tao_staked, alpha, netuid, )); @@ -676,12 +699,12 @@ impl<T: Config> Pallet<T> { "StakeAdded( coldkey: {:?}, hotkey:{:?}, tao: {:?}, alpha:{:?}, netuid: {:?} )", coldkey.clone(), hotkey.clone(), - tao, + tao_staked, alpha, netuid ); - // Step 6: Return the amount of alpha staked + // Step 7: Return the amount of alpha staked alpha } diff --git a/pallets/subtensor/src/tests/staking.rs b/pallets/subtensor/src/tests/staking.rs index 6594887b9..882184811 100644 --- a/pallets/subtensor/src/tests/staking.rs +++ b/pallets/subtensor/src/tests/staking.rs @@ -1941,3 +1941,100 @@ fn test_staking_too_little_fails() { ); }); } + +// cargo test --package pallet-subtensor --lib -- tests::staking::test_add_stake_fee_goes_to_subnet_tao --exact --show-output --nocapture +#[test] +fn test_add_stake_fee_goes_to_subnet_tao() { + new_test_ext(1).execute_with(|| { + let subnet_owner_coldkey = U256::from(1001); + let subnet_owner_hotkey = U256::from(1002); + let hotkey = U256::from(2); + let coldkey = U256::from(3); + let existential_deposit = ExistentialDeposit::get(); + let tao_to_stake = DefaultMinStake::<Test>::get() * 10; + let fee = DefaultMinStake::<Test>::get(); + + let netuid = add_dynamic_network(&subnet_owner_hotkey, &subnet_owner_coldkey); + SubtensorModule::create_account_if_non_existent(&coldkey, &hotkey); + let subnet_tao_before = SubnetTAO::<Test>::get(netuid); + + // Add stake + SubtensorModule::add_balance_to_coldkey_account(&coldkey, tao_to_stake); + assert_ok!(SubtensorModule::add_stake( + RuntimeOrigin::signed(coldkey), + hotkey, + netuid, + tao_to_stake + )); + + // Calculate expected stake + let expected_alpha = tao_to_stake - existential_deposit - fee; + let actual_alpha = SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet(&hotkey, &coldkey, netuid); + let subnet_tao_after = SubnetTAO::<Test>::get(netuid); + + // Total subnet stake should match the sum of delegators' stakes minus existential deposits. + assert_abs_diff_eq!( + actual_alpha, + expected_alpha, + epsilon = expected_alpha / 1000 + ); + + // Subnet TAO should have increased by the full tao_to_stake amount + assert_abs_diff_eq!( + subnet_tao_before + tao_to_stake, + subnet_tao_after, + epsilon = 10 + ); + }); +} + +// cargo test --package pallet-subtensor --lib -- tests::staking::test_remove_stake_fee_goes_to_subnet_tao --exact --show-output --nocapture +#[test] +fn test_remove_stake_fee_goes_to_subnet_tao() { + new_test_ext(1).execute_with(|| { + let subnet_owner_coldkey = U256::from(1001); + let subnet_owner_hotkey = U256::from(1002); + let hotkey = U256::from(2); + let coldkey = U256::from(3); + let tao_to_stake = DefaultMinStake::<Test>::get() * 10; + let fee = DefaultMinStake::<Test>::get(); + + let netuid = add_dynamic_network(&subnet_owner_hotkey, &subnet_owner_coldkey); + SubtensorModule::create_account_if_non_existent(&coldkey, &hotkey); + let subnet_tao_before = SubnetTAO::<Test>::get(netuid); + + // Add stake + SubtensorModule::add_balance_to_coldkey_account(&coldkey, tao_to_stake); + assert_ok!(SubtensorModule::add_stake( + RuntimeOrigin::signed(coldkey), + hotkey, + netuid, + tao_to_stake + )); + + // Remove all stake + let alpha_to_unstake = SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet(&hotkey, &coldkey, netuid); + assert_ok!(SubtensorModule::remove_stake( + RuntimeOrigin::signed(coldkey), + hotkey, + netuid, + alpha_to_unstake + )); + let subnet_tao_after = SubnetTAO::<Test>::get(netuid); + + // Subnet TAO should have increased by 2x fee as a result of staking + unstaking + assert_abs_diff_eq!( + subnet_tao_before + 2 * fee, + subnet_tao_after, + epsilon = alpha_to_unstake / 1000 + ); + + // User balance should decrease by 2x fee as a result of staking + unstaking + let balance_after = SubtensorModule::get_coldkey_balance(&coldkey); + assert_abs_diff_eq!( + balance_after + 2 * fee, + tao_to_stake, + epsilon = tao_to_stake / 1000 + ); + }); +} \ No newline at end of file From 01203b329539c81ecd4dded6c14332bf1fb0c70f Mon Sep 17 00:00:00 2001 From: camfairchild <cameron@opentensor.dev> Date: Tue, 21 Jan 2025 15:52:45 -0500 Subject: [PATCH 030/145] add alpha swapped tracker --- .../subtensor/src/coinbase/run_coinbase.rs | 22 +++++++++++++++---- pallets/subtensor/src/lib.rs | 4 ++++ 2 files changed, 22 insertions(+), 4 deletions(-) diff --git a/pallets/subtensor/src/coinbase/run_coinbase.rs b/pallets/subtensor/src/coinbase/run_coinbase.rs index 259bf68cd..e86c07c24 100644 --- a/pallets/subtensor/src/coinbase/run_coinbase.rs +++ b/pallets/subtensor/src/coinbase/run_coinbase.rs @@ -231,6 +231,10 @@ impl<T: Config> Pallet<T> { PendingRootDivs::<T>::mutate(*netuid, |total| { *total = total.saturating_add(root_emission_in_tao); }); + // Accumulate alpha that was swapped for the pending root divs. + PendingAlphaSwapped::<T>::mutate(*netuid, |total| { + *total = total.saturating_add(root_emission_in_alpha.to_num::<u64>()); + }); // Accumulate alpha emission in pending. PendingEmission::<T>::mutate(*netuid, |total| { *total = total.saturating_add(pending_alpha_emission.to_num::<u64>()); @@ -261,10 +265,14 @@ impl<T: Config> Pallet<T> { let pending_emission: u64 = PendingEmission::<T>::get(netuid); PendingEmission::<T>::insert(netuid, 0); - // 5.2.2 Get and drain the subnet pending root divs. + // 5.2.2a Get and drain the subnet pending root divs. let pending_root_divs: u64 = PendingRootDivs::<T>::get(netuid); PendingRootDivs::<T>::insert(netuid, 0); + // 5.2.2b Get this amount as alpha that was swapped for pending root divs. + let pending_alpha_swapped: u64 = PendingAlphaSwapped::<T>::get(netuid); + PendingAlphaSwapped::<T>::insert(netuid, 0); + // 5.2.3 Get owner cut and drain. let owner_cut: u64 = PendingOwnerCut::<T>::get(netuid); PendingOwnerCut::<T>::insert(netuid, 0); @@ -274,6 +282,7 @@ impl<T: Config> Pallet<T> { netuid, pending_emission, pending_root_divs, + pending_alpha_swapped, owner_cut, ); } else { @@ -287,19 +296,24 @@ impl<T: Config> Pallet<T> { netuid: u16, pending_alpha_emission: u64, pending_root_divs: u64, + pending_alpha_swapped: u64, owner_cut: u64, ) { log::debug!( - "Draining pending alpha emission for netuid {:?}: {:?}, with pending root divs {:?}, and owner cut {:?}", + "Draining pending alpha emission for netuid {:?}: {:?}, with pending root divs {:?}, pending alpha swapped {:?}, and owner cut {:?}", netuid, pending_alpha_emission, pending_root_divs, + pending_alpha_swapped, owner_cut ); // Run the epoch() --> hotkey emission. - let hotkey_emission: Vec<(T::AccountId, u64, u64)> = - Self::epoch(netuid, pending_alpha_emission); + // Needs to run on the full emission to the subnet. + let hotkey_emission: Vec<(T::AccountId, u64, u64)> = Self::epoch( + netuid, + pending_alpha_emission.saturating_add(pending_alpha_swapped), + ); log::debug!( "Hotkey emission for netuid {:?}: {:?}", netuid, diff --git a/pallets/subtensor/src/lib.rs b/pallets/subtensor/src/lib.rs index 4a2150f7f..1d2690503 100644 --- a/pallets/subtensor/src/lib.rs +++ b/pallets/subtensor/src/lib.rs @@ -1097,6 +1097,10 @@ pub mod pallet { /// --- MAP ( netuid ) --> pending_root_emission pub type PendingRootDivs<T> = StorageMap<_, Identity, u16, u64, ValueQuery, DefaultZeroU64<T>>; #[pallet::storage] + /// --- MAP ( netuid ) --> pending_alpha_swapped + pub type PendingAlphaSwapped<T> = + StorageMap<_, Identity, u16, u64, ValueQuery, DefaultZeroU64<T>>; + #[pallet::storage] /// --- MAP ( netuid ) --> pending_owner_cut pub type PendingOwnerCut<T> = StorageMap<_, Identity, u16, u64, ValueQuery, DefaultZeroU64<T>>; #[pallet::storage] From f0f4c6b990dccb5cf7e9bdd6dc8f3e0dbf7f9fc7 Mon Sep 17 00:00:00 2001 From: JohnReedV <87283488+JohnReedV@users.noreply.github.com> Date: Tue, 21 Jan 2025 14:34:46 -0800 Subject: [PATCH 031/145] add transfer_stake & tests --- pallets/subtensor/src/macros/dispatches.rs | 44 ++++ pallets/subtensor/src/macros/events.rs | 5 + pallets/subtensor/src/staking/move_stake.rs | 107 +++++++++ pallets/subtensor/src/tests/move_stake.rs | 253 ++++++++++++++++++++ 4 files changed, 409 insertions(+) diff --git a/pallets/subtensor/src/macros/dispatches.rs b/pallets/subtensor/src/macros/dispatches.rs index 7073a1413..34712ccb5 100644 --- a/pallets/subtensor/src/macros/dispatches.rs +++ b/pallets/subtensor/src/macros/dispatches.rs @@ -1596,5 +1596,49 @@ mod dispatches { alpha_amount, ) } + + /// Transfers a specified amount of stake from one coldkey to another, optionally across subnets, + /// while keeping the same hotkey. + /// + /// # Arguments + /// * `origin` - The origin of the transaction, which must be signed by the `origin_coldkey`. + /// * `destination_coldkey` - The coldkey to which the stake is transferred. + /// * `hotkey` - The hotkey associated with the stake. + /// * `origin_netuid` - The network/subnet ID to move stake from. + /// * `destination_netuid` - The network/subnet ID to move stake to (for cross-subnet transfer). + /// * `alpha_amount` - The amount of stake to transfer. + /// + /// # Weight + /// Uses a fixed weight of 3_000_000 (plus any DB write overhead). + /// + /// # Errors + /// Returns an error if: + /// * The origin is not signed by the correct coldkey. + /// * Either subnet does not exist. + /// * The hotkey does not exist. + /// * There is insufficient stake on `(origin_coldkey, hotkey, origin_netuid)`. + /// * The transfer amount is below the minimum stake requirement. + /// + /// # Events + /// May emit a `StakeTransferred` event on success. + #[pallet::call_index(86)] + #[pallet::weight((Weight::from_parts(3_000_000, 0).saturating_add(T::DbWeight::get().writes(1)), DispatchClass::Operational, Pays::No))] + pub fn transfer_stake( + origin: T::RuntimeOrigin, + destination_coldkey: T::AccountId, + hotkey: T::AccountId, + origin_netuid: u16, + destination_netuid: u16, + alpha_amount: u64, + ) -> DispatchResult { + Self::do_transfer_stake( + origin, + destination_coldkey, + hotkey, + origin_netuid, + destination_netuid, + alpha_amount, + ) + } } } diff --git a/pallets/subtensor/src/macros/events.rs b/pallets/subtensor/src/macros/events.rs index 30566ac47..782f6b792 100644 --- a/pallets/subtensor/src/macros/events.rs +++ b/pallets/subtensor/src/macros/events.rs @@ -248,5 +248,10 @@ mod events { /// /// - **error**: The dispatch error emitted by the failed item. BatchWeightItemFailed(sp_runtime::DispatchError), + + /// Stake has been transferred from one coldkey to another on the same subnet. + /// Parameters: + /// (origin_coldkey, destination_coldkey, hotkey, origin_netuid, destination_netuid, amount) + StakeTransferred(T::AccountId, T::AccountId, T::AccountId, u16, u16, u64), } } diff --git a/pallets/subtensor/src/staking/move_stake.rs b/pallets/subtensor/src/staking/move_stake.rs index 9628c5814..49d823b86 100644 --- a/pallets/subtensor/src/staking/move_stake.rs +++ b/pallets/subtensor/src/staking/move_stake.rs @@ -110,4 +110,111 @@ impl<T: Config> Pallet<T> { // -- 10. Ok and return. Ok(()) } + + /// Transfers stake from one coldkey to another, optionally moving from one subnet to another, + /// while keeping the same hotkey. + /// + /// # Arguments + /// * `origin` - The origin of the transaction, which must be signed by the `origin_coldkey`. + /// * `destination_coldkey` - The account ID of the coldkey to which the stake is being transferred. + /// * `hotkey` - The account ID of the hotkey associated with this stake. + /// * `origin_netuid` - The network ID (subnet) from which the stake is being transferred. + /// * `destination_netuid` - The network ID (subnet) to which the stake is being transferred. + /// * `alpha_amount` - The amount of stake to transfer. + /// + /// # Returns + /// * `DispatchResult` - Indicates success or failure. + /// + /// # Errors + /// This function will return an error if: + /// * The transaction is not signed by the `origin_coldkey`. + /// * The subnet (`origin_netuid` or `destination_netuid`) does not exist. + /// * The `hotkey` does not exist. + /// * The `(origin_coldkey, hotkey, origin_netuid)` does not have enough stake for `alpha_amount`. + /// * The amount to be transferred is below the minimum stake requirement. + /// * There is a failure in staking or unstaking logic. + /// + /// # Events + /// Emits a `StakeTransferred` event upon successful completion of the transfer. + pub fn do_transfer_stake( + origin: T::RuntimeOrigin, + destination_coldkey: T::AccountId, + hotkey: T::AccountId, + origin_netuid: u16, + destination_netuid: u16, + alpha_amount: u64, + ) -> dispatch::DispatchResult { + // 1. Ensure the extrinsic is signed by the origin_coldkey. + let coldkey = ensure_signed(origin)?; + + // 2. Ensure both subnets exist. + ensure!( + Self::if_subnet_exist(origin_netuid), + Error::<T>::SubnetNotExists + ); + ensure!( + Self::if_subnet_exist(destination_netuid), + Error::<T>::SubnetNotExists + ); + + // 3. Check that the hotkey exists. + ensure!( + Self::hotkey_account_exists(&hotkey), + Error::<T>::HotKeyAccountNotExists + ); + + // 4. Check that the signed coldkey actually owns the given hotkey. + ensure!( + Self::coldkey_owns_hotkey(&coldkey, &hotkey), + Error::<T>::NonAssociatedColdKey + ); + + // 5. Get current stake. + let origin_alpha = + Self::get_stake_for_hotkey_and_coldkey_on_subnet(&hotkey, &coldkey, origin_netuid); + ensure!( + alpha_amount <= origin_alpha, + Error::<T>::NotEnoughStakeToWithdraw + ); + + // 6. Unstake from the origin coldkey; this returns an amount of TAO. + let origin_tao = Self::unstake_from_subnet(&hotkey, &coldkey, origin_netuid, alpha_amount); + + // 7. Ensure the returned TAO meets a minimum stake requirement (if required). + ensure!( + origin_tao >= DefaultMinStake::<T>::get(), + Error::<T>::AmountTooLow + ); + + // 8. Stake the TAO into `(destination_coldkey, hotkey)` on the destination subnet. + // Create the account if it does not exist. + Self::stake_into_subnet( + &hotkey, + &destination_coldkey, + destination_netuid, + origin_tao, + ); + + // 9. Emit an event for logging/monitoring. + log::info!( + "StakeTransferred(origin_coldkey: {:?}, destination_coldkey: {:?}, hotkey: {:?}, origin_netuid: {:?}, destination_netuid: {:?}, amount: {:?})", + coldkey, + destination_coldkey, + hotkey, + origin_netuid, + destination_netuid, + origin_tao + ); + Self::deposit_event(Event::StakeTransferred( + coldkey, + destination_coldkey, + hotkey, + origin_netuid, + destination_netuid, + origin_tao, + )); + + // 10. Return success. + Ok(()) + } } diff --git a/pallets/subtensor/src/tests/move_stake.rs b/pallets/subtensor/src/tests/move_stake.rs index f26a82432..59afc9b62 100644 --- a/pallets/subtensor/src/tests/move_stake.rs +++ b/pallets/subtensor/src/tests/move_stake.rs @@ -773,3 +773,256 @@ fn test_moving_too_little_fails() { ); }); } + +#[test] +fn test_do_transfer_success() { + new_test_ext(1).execute_with(|| { + // 1. Create a new dynamic network and IDs. + 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); + + // 2. Define the origin coldkey, destination coldkey, and hotkey to be used. + let origin_coldkey = U256::from(1); + let destination_coldkey = U256::from(2); + let hotkey = U256::from(3); + let stake_amount = DefaultMinStake::<Test>::get() * 10; + + // 3. Set up initial stake: (origin_coldkey, hotkey) on netuid. + SubtensorModule::create_account_if_non_existent(&origin_coldkey, &hotkey); + SubtensorModule::create_account_if_non_existent(&destination_coldkey, &hotkey); + SubtensorModule::stake_into_subnet(&hotkey, &origin_coldkey, netuid, stake_amount); + let alpha = SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( + &hotkey, + &origin_coldkey, + netuid, + ); + + // 4. Transfer the entire stake to the destination coldkey on the same subnet (netuid, netuid). + assert_ok!(SubtensorModule::do_transfer_stake( + RuntimeOrigin::signed(origin_coldkey), + destination_coldkey, + hotkey, + netuid, + netuid, + alpha + )); + + // 5. Check that the stake has moved. + assert_eq!( + SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( + &hotkey, + &origin_coldkey, + netuid + ), + 0 + ); + assert_abs_diff_eq!( + SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( + &hotkey, + &destination_coldkey, + netuid + ), + stake_amount, + epsilon = stake_amount / 1000 + ); + }); +} + +#[test] +fn test_do_transfer_nonexistent_subnet() { + new_test_ext(1).execute_with(|| { + let origin_coldkey = U256::from(1); + let destination_coldkey = U256::from(2); + let hotkey = U256::from(3); + let nonexistent_netuid = 9999; + let stake_amount = DefaultMinStake::<Test>::get() * 5; + + assert_noop!( + SubtensorModule::do_transfer_stake( + RuntimeOrigin::signed(origin_coldkey), + destination_coldkey, + hotkey, + nonexistent_netuid, + nonexistent_netuid, + stake_amount + ), + Error::<Test>::SubnetNotExists + ); + }); +} + +#[test] +fn test_do_transfer_nonexistent_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 origin_coldkey = U256::from(1); + let destination_coldkey = U256::from(2); + let nonexistent_hotkey = U256::from(999); + + assert_noop!( + SubtensorModule::do_transfer_stake( + RuntimeOrigin::signed(origin_coldkey), + destination_coldkey, + nonexistent_hotkey, + netuid, + netuid, + 100 + ), + Error::<Test>::HotKeyAccountNotExists + ); + }); +} + +#[test] +fn test_do_transfer_insufficient_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 origin_coldkey = U256::from(1); + let destination_coldkey = U256::from(2); + let hotkey = U256::from(3); + let stake_amount = DefaultMinStake::<Test>::get() * 10; + + SubtensorModule::create_account_if_non_existent(&origin_coldkey, &hotkey); + SubtensorModule::stake_into_subnet(&hotkey, &origin_coldkey, netuid, stake_amount); + + let alpha = stake_amount * 2; + assert_noop!( + SubtensorModule::do_transfer_stake( + RuntimeOrigin::signed(origin_coldkey), + destination_coldkey, + hotkey, + netuid, + netuid, + alpha + ), + Error::<Test>::NotEnoughStakeToWithdraw + ); + }); +} + +#[test] +fn test_do_transfer_wrong_origin() { + new_test_ext(1).execute_with(|| { + let subnet_owner_coldkey = U256::from(1010); + let subnet_owner_hotkey = U256::from(1011); + let netuid = add_dynamic_network(&subnet_owner_hotkey, &subnet_owner_coldkey); + + let origin_coldkey = U256::from(1); + let wrong_coldkey = U256::from(9999); + let destination_coldkey = U256::from(2); + let hotkey = U256::from(3); + let stake_amount = 100_000; + + SubtensorModule::create_account_if_non_existent(&origin_coldkey, &hotkey); + SubtensorModule::add_balance_to_coldkey_account(&origin_coldkey, 1_000_000_000); + SubtensorModule::stake_into_subnet(&hotkey, &origin_coldkey, netuid, stake_amount); + + assert_noop!( + SubtensorModule::do_transfer_stake( + RuntimeOrigin::signed(wrong_coldkey), + destination_coldkey, + hotkey, + netuid, + netuid, + stake_amount + ), + Error::<Test>::NonAssociatedColdKey + ); + }); +} + +#[test] +fn test_do_transfer_minimum_stake_check() { + 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 origin_coldkey = U256::from(1); + let destination_coldkey = U256::from(2); + let hotkey = U256::from(3); + + let stake_amount = DefaultMinStake::<Test>::get(); + SubtensorModule::create_account_if_non_existent(&origin_coldkey, &hotkey); + SubtensorModule::stake_into_subnet(&hotkey, &origin_coldkey, netuid, stake_amount); + + assert_err!( + SubtensorModule::do_transfer_stake( + RuntimeOrigin::signed(origin_coldkey), + destination_coldkey, + hotkey, + netuid, + netuid, + 1 + ), + Error::<Test>::AmountTooLow + ); + }); +} + +#[test] +fn test_do_transfer_different_subnets() { + new_test_ext(1).execute_with(|| { + // 1. Create two distinct subnets. + 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); + + // 2. Define origin/destination coldkeys and hotkey. + let origin_coldkey = U256::from(1); + let destination_coldkey = U256::from(2); + let hotkey = U256::from(3); + let stake_amount = DefaultMinStake::<Test>::get() * 10; + + // 3. Create accounts if needed. + SubtensorModule::create_account_if_non_existent(&origin_coldkey, &hotkey); + SubtensorModule::create_account_if_non_existent(&destination_coldkey, &hotkey); + + // 4. Deposit free balance so transaction fees do not reduce staked funds. + SubtensorModule::add_balance_to_coldkey_account(&origin_coldkey, 1_000_000_000); + + // 5. Stake into the origin subnet. + SubtensorModule::stake_into_subnet(&hotkey, &origin_coldkey, origin_netuid, stake_amount); + + // 6. Transfer entire stake from origin_netuid -> destination_netuid. + let alpha = SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( + &hotkey, + &origin_coldkey, + origin_netuid, + ); + assert_ok!(SubtensorModule::do_transfer_stake( + RuntimeOrigin::signed(origin_coldkey), + destination_coldkey, + hotkey, + origin_netuid, + destination_netuid, + alpha + )); + + // 7. Verify origin now has 0 in origin_netuid. + assert_eq!( + SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( + &hotkey, + &origin_coldkey, + origin_netuid + ), + 0 + ); + + // 8. Verify stake ended up in destination subnet for destination coldkey. + let dest_stake = SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( + &hotkey, + &destination_coldkey, + destination_netuid, + ); + assert_abs_diff_eq!(dest_stake, stake_amount, epsilon = stake_amount / 1000); + }); +} From a516c2b6de51cfb1fbb59bceef2da27b93ccd65b Mon Sep 17 00:00:00 2001 From: Greg Zaitsev <gregz@opentensor.dev> Date: Tue, 21 Jan 2025 19:22:51 -0500 Subject: [PATCH 032/145] Implement fees for add_stake, remove_stake, and move_stake --- pallets/subtensor/src/lib.rs | 42 +++- pallets/subtensor/src/staking/add_stake.rs | 3 +- pallets/subtensor/src/staking/helpers.rs | 3 +- pallets/subtensor/src/staking/move_stake.rs | 5 +- pallets/subtensor/src/staking/remove_stake.rs | 17 +- pallets/subtensor/src/staking/stake_utils.rs | 14 +- pallets/subtensor/src/tests/mock.rs | 3 +- pallets/subtensor/src/tests/move_stake.rs | 90 +++++--- pallets/subtensor/src/tests/senate.rs | 30 +-- pallets/subtensor/src/tests/staking.rs | 197 +++++++++++++----- pallets/subtensor/src/tests/swap_coldkey.rs | 51 +++-- pallets/subtensor/src/tests/swap_hotkey.rs | 5 +- pallets/subtensor/src/tests/weights.rs | 12 +- 13 files changed, 326 insertions(+), 146 deletions(-) diff --git a/pallets/subtensor/src/lib.rs b/pallets/subtensor/src/lib.rs index 7f75568ba..58aef11a8 100644 --- a/pallets/subtensor/src/lib.rs +++ b/pallets/subtensor/src/lib.rs @@ -12,7 +12,10 @@ use frame_support::{ dispatch::{self, DispatchInfo, DispatchResult, DispatchResultWithPostInfo, PostDispatchInfo}, ensure, pallet_macros::import_section, - traits::{tokens::fungible, IsSubType}, + traits::{ + tokens::{fungible, Fortitude, Preservation}, + IsSubType, + }, }; use codec::{Decode, Encode}; @@ -21,6 +24,7 @@ use frame_support::sp_runtime::transaction_validity::ValidTransaction; use pallet_balances::Call as BalancesCall; // use pallet_scheduler as Scheduler; use scale_info::TypeInfo; +use sp_core::Get; use sp_runtime::{ traits::{DispatchInfoOf, Dispatchable, PostDispatchInfoOf, SignedExtension}, transaction_validity::{TransactionValidity, TransactionValidityError}, @@ -697,9 +701,10 @@ pub mod pallet { #[pallet::type_value] /// Default minimum stake. - /// 2M rao matches $1 at $500/TAO + /// 500k rao matches $0.25 at $500/TAO + /// Also used as staking fee pub fn DefaultMinStake<T: Config>() -> u64 { - 2_000_000 + 500_000 } #[pallet::type_value] @@ -1532,12 +1537,14 @@ pub enum CallType { #[derive(Debug, PartialEq)] pub enum CustomTransactionError { ColdkeyInSwapSchedule, + StakeAmountTooLow, } impl From<CustomTransactionError> for u8 { fn from(variant: CustomTransactionError) -> u8 { match variant { CustomTransactionError::ColdkeyInSwapSchedule => 0, + CustomTransactionError::StakeAmountTooLow => 1, } } } @@ -1687,10 +1694,31 @@ where Err(InvalidTransaction::Custom(7).into()) } } - Some(Call::add_stake { .. }) => Ok(ValidTransaction { - priority: Self::get_priority_vanilla(), - ..Default::default() - }), + Some(Call::add_stake { + hotkey: _, + netuid: _, + amount_staked, + }) => { + // Check that amount parameter is at least the min stake + // also check the coldkey balance + let coldkey_balance = <<T as Config>::Currency as fungible::Inspect< + <T as frame_system::Config>::AccountId, + >>::reducible_balance( + who, Preservation::Expendable, Fortitude::Polite + ); + + if (*amount_staked < DefaultMinStake::<T>::get()) + || (coldkey_balance < DefaultMinStake::<T>::get()) + { + InvalidTransaction::Custom(CustomTransactionError::StakeAmountTooLow.into()) + .into() + } else { + Ok(ValidTransaction { + priority: Self::get_priority_vanilla(), + ..Default::default() + }) + } + } Some(Call::remove_stake { .. }) => Ok(ValidTransaction { priority: Self::get_priority_vanilla(), ..Default::default() diff --git a/pallets/subtensor/src/staking/add_stake.rs b/pallets/subtensor/src/staking/add_stake.rs index f63735cd3..66b337b3d 100644 --- a/pallets/subtensor/src/staking/add_stake.rs +++ b/pallets/subtensor/src/staking/add_stake.rs @@ -74,7 +74,8 @@ impl<T: Config> Pallet<T> { // 6. Swap the stake into alpha on the subnet and increase counters. // Emit the staking event. - Self::stake_into_subnet(&hotkey, &coldkey, netuid, tao_staked); + let fee = DefaultMinStake::<T>::get(); + Self::stake_into_subnet(&hotkey, &coldkey, netuid, tao_staked, fee); // Ok and return. Ok(()) diff --git a/pallets/subtensor/src/staking/helpers.rs b/pallets/subtensor/src/staking/helpers.rs index 4f99d954e..1a1a2d00d 100644 --- a/pallets/subtensor/src/staking/helpers.rs +++ b/pallets/subtensor/src/staking/helpers.rs @@ -163,7 +163,8 @@ impl<T: Config> Pallet<T> { // Log the clearing of a small nomination // Remove the stake from the nominator account. (this is a more forceful unstake operation which ) // Actually deletes the staking account. - let cleared_stake = Self::unstake_from_subnet(hotkey, coldkey, netuid, stake); + // Do not apply any fees + let cleared_stake = Self::unstake_from_subnet(hotkey, coldkey, netuid, stake, 0); // Add the stake to the coldkey account. Self::add_balance_to_coldkey_account(coldkey, cleared_stake); } diff --git a/pallets/subtensor/src/staking/move_stake.rs b/pallets/subtensor/src/staking/move_stake.rs index 9628c5814..6fbd0db07 100644 --- a/pallets/subtensor/src/staking/move_stake.rs +++ b/pallets/subtensor/src/staking/move_stake.rs @@ -68,11 +68,13 @@ impl<T: Config> Pallet<T> { ); // --- 7. Unstake the amount of alpha from the origin subnet, converting it to TAO + let fee = DefaultMinStake::<T>::get().saturating_div(2); // fee is half of min stake because it is applied twice let origin_tao = Self::unstake_from_subnet( &origin_hotkey.clone(), &coldkey.clone(), origin_netuid, alpha_amount, + fee, ); // Ensure origin_tao is at least DefaultMinStake @@ -87,6 +89,7 @@ impl<T: Config> Pallet<T> { &coldkey.clone(), destination_netuid, origin_tao, + fee, ); // --- 9. Log the event. @@ -104,7 +107,7 @@ impl<T: Config> Pallet<T> { origin_netuid, destination_hotkey, destination_netuid, - origin_tao, + origin_tao.saturating_sub(fee), )); // -- 10. Ok and return. diff --git a/pallets/subtensor/src/staking/remove_stake.rs b/pallets/subtensor/src/staking/remove_stake.rs index 5578219d3..5b3ed3390 100644 --- a/pallets/subtensor/src/staking/remove_stake.rs +++ b/pallets/subtensor/src/staking/remove_stake.rs @@ -1,4 +1,5 @@ use super::*; +use sp_core::Get; impl<T: Config> Pallet<T> { /// ---- The implementation for the extrinsic remove_stake: Removes stake from a hotkey account and adds it onto a coldkey. @@ -65,8 +66,9 @@ impl<T: Config> Pallet<T> { ); // 6. Swap the alpba to tao and update counters for this subnet. + let fee = DefaultMinStake::<T>::get(); let tao_unstaked: u64 = - Self::unstake_from_subnet(&hotkey, &coldkey, netuid, alpha_unstaked); + Self::unstake_from_subnet(&hotkey, &coldkey, netuid, alpha_unstaked, fee); // 7. We add the balance to the coldkey. If the above fails we will not credit this coldkey. Self::add_balance_to_coldkey_account(&coldkey, tao_unstaked); @@ -81,10 +83,6 @@ impl<T: Config> Pallet<T> { }) } - // TODO: Regression - // Emit the unstaking event. - // Self::deposit_event(Event::StakeRemoved(hotkey, stake_to_be_removed)); - // Done and ok. Ok(()) } @@ -119,6 +117,8 @@ impl<T: Config> Pallet<T> { origin: T::RuntimeOrigin, hotkey: T::AccountId, ) -> dispatch::DispatchResult { + let fee = DefaultMinStake::<T>::get(); + // 1. We check the transaction is signed by the caller and retrieve the T::AccountId coldkey information. let coldkey = ensure_signed(origin)?; log::info!("do_unstake_all( origin:{:?} hotkey:{:?} )", coldkey, hotkey); @@ -141,7 +141,7 @@ impl<T: Config> Pallet<T> { if alpha_unstaked > 0 { // Swap the alpha to tao and update counters for this subnet. let tao_unstaked: u64 = - Self::unstake_from_subnet(&hotkey, &coldkey, *netuid, alpha_unstaked); + Self::unstake_from_subnet(&hotkey, &coldkey, *netuid, alpha_unstaked, fee); // Add the balance to the coldkey. If the above fails we will not credit this coldkey. Self::add_balance_to_coldkey_account(&coldkey, tao_unstaked); @@ -185,6 +185,8 @@ impl<T: Config> Pallet<T> { origin: T::RuntimeOrigin, hotkey: T::AccountId, ) -> dispatch::DispatchResult { + let fee = DefaultMinStake::<T>::get(); + // 1. We check the transaction is signed by the caller and retrieve the T::AccountId coldkey information. let coldkey = ensure_signed(origin)?; log::info!("do_unstake_all( origin:{:?} hotkey:{:?} )", coldkey, hotkey); @@ -210,7 +212,7 @@ impl<T: Config> Pallet<T> { if alpha_unstaked > 0 { // Swap the alpha to tao and update counters for this subnet. let tao_unstaked: u64 = - Self::unstake_from_subnet(&hotkey, &coldkey, *netuid, alpha_unstaked); + Self::unstake_from_subnet(&hotkey, &coldkey, *netuid, alpha_unstaked, fee); // Increment total total_tao_unstaked = total_tao_unstaked.saturating_add(tao_unstaked); @@ -227,6 +229,7 @@ impl<T: Config> Pallet<T> { &coldkey, Self::get_root_netuid(), total_tao_unstaked, + 0, // no fee for restaking ); // 5. Done and ok. diff --git a/pallets/subtensor/src/staking/stake_utils.rs b/pallets/subtensor/src/staking/stake_utils.rs index b79b141d0..d6901eec2 100644 --- a/pallets/subtensor/src/staking/stake_utils.rs +++ b/pallets/subtensor/src/staking/stake_utils.rs @@ -1,7 +1,5 @@ use super::*; -use crate::DefaultMinStake; use share_pool::{SharePool, SharePoolDataOperations}; -use sp_core::Get; use sp_std::ops::Neg; use substrate_fixed::types::{I64F64, I96F32, U64F64}; @@ -608,6 +606,7 @@ impl<T: Config> Pallet<T> { coldkey: &T::AccountId, netuid: u16, alpha: u64, + fee: u64, ) -> u64 { // Step 1: Swap the alpha for TAO. let tao: u64 = Self::swap_alpha_for_tao(netuid, alpha); @@ -624,12 +623,14 @@ impl<T: Config> Pallet<T> { // } // Step 4. Reduce tao amount by staking fee and credit this fee to SubnetTAO - let fee = DefaultMinStake::<T>::get(); let tao_unstaked = tao.saturating_sub(fee); let actual_fee = tao.saturating_sub(tao_unstaked); SubnetTAO::<T>::mutate(netuid, |total| { *total = total.saturating_add(actual_fee); }); + TotalStake::<T>::mutate(|total| { + *total = total.saturating_add(actual_fee); + }); // Step 5. Deposit and log the unstaking event. Self::deposit_event(Event::StakeRemoved( @@ -660,11 +661,11 @@ impl<T: Config> Pallet<T> { coldkey: &T::AccountId, netuid: u16, tao: u64, + fee: u64, ) -> u64 { // Step 1. Reduce tao amount by staking fee and credit this fee to SubnetTAO - // At this point tao was already withdrawn from the user balance and is considered + // At this point tao was already withdrawn from the user balance and is considered // available - let fee = DefaultMinStake::<T>::get(); let tao_staked = tao.saturating_sub(fee); let actual_fee = tao.saturating_sub(tao_staked); @@ -686,6 +687,9 @@ impl<T: Config> Pallet<T> { SubnetTAO::<T>::mutate(netuid, |total| { *total = total.saturating_add(actual_fee); }); + TotalStake::<T>::mutate(|total| { + *total = total.saturating_add(actual_fee); + }); // Step 6. Deposit and log the staking event. Self::deposit_event(Event::StakeAdded( diff --git a/pallets/subtensor/src/tests/mock.rs b/pallets/subtensor/src/tests/mock.rs index a6f9caed7..9731113e5 100644 --- a/pallets/subtensor/src/tests/mock.rs +++ b/pallets/subtensor/src/tests/mock.rs @@ -726,7 +726,8 @@ pub fn increase_stake_on_coldkey_hotkey_account( tao_staked: u64, netuid: u16, ) { - SubtensorModule::stake_into_subnet(hotkey, coldkey, netuid, tao_staked); + let fee = 0; + SubtensorModule::stake_into_subnet(hotkey, coldkey, netuid, tao_staked, fee); } /// Increases the stake on the hotkey account under its owning coldkey. diff --git a/pallets/subtensor/src/tests/move_stake.rs b/pallets/subtensor/src/tests/move_stake.rs index f26a82432..f46327305 100644 --- a/pallets/subtensor/src/tests/move_stake.rs +++ b/pallets/subtensor/src/tests/move_stake.rs @@ -17,11 +17,12 @@ fn test_do_move_success() { let origin_hotkey = U256::from(2); let destination_hotkey = U256::from(3); let stake_amount = DefaultMinStake::<Test>::get() * 10; + let fee = DefaultMinStake::<Test>::get(); // 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); + SubtensorModule::stake_into_subnet(&origin_hotkey, &coldkey, netuid, stake_amount, fee); let alpha = SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( &origin_hotkey, &coldkey, @@ -53,7 +54,7 @@ fn test_do_move_success() { &coldkey, netuid ), - stake_amount, + stake_amount - 2 * fee, epsilon = stake_amount / 1000 ); }); @@ -73,11 +74,18 @@ fn test_do_move_different_subnets() { let origin_hotkey = U256::from(2); let destination_hotkey = U256::from(3); let stake_amount = DefaultMinStake::<Test>::get() * 10; + let fee = DefaultMinStake::<Test>::get(); // 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); + SubtensorModule::stake_into_subnet( + &origin_hotkey, + &coldkey, + origin_netuid, + stake_amount, + fee, + ); let alpha = SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( &origin_hotkey, &coldkey, @@ -109,7 +117,7 @@ fn test_do_move_different_subnets() { &coldkey, destination_netuid ), - stake_amount, + stake_amount - 2 * fee, epsilon = stake_amount / 1000 ); }); @@ -129,9 +137,16 @@ fn test_do_move_nonexistent_subnet() { let destination_hotkey = U256::from(3); let nonexistent_netuid = 99; // Assuming this subnet doesn't exist let stake_amount = 1_000_000; + let fee = 0; // Set up initial stake - SubtensorModule::stake_into_subnet(&origin_hotkey, &coldkey, origin_netuid, stake_amount); + SubtensorModule::stake_into_subnet( + &origin_hotkey, + &coldkey, + origin_netuid, + stake_amount, + fee, + ); let alpha = SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( &origin_hotkey, &coldkey, @@ -221,9 +236,10 @@ fn test_do_move_nonexistent_destination_hotkey() { let nonexistent_destination_hotkey = U256::from(99); // Assuming this hotkey doesn't exist let netuid = 1; let stake_amount = 1_000_000; + let fee = 0; // Set up initial stake - SubtensorModule::stake_into_subnet(&origin_hotkey, &coldkey, netuid, stake_amount); + SubtensorModule::stake_into_subnet(&origin_hotkey, &coldkey, netuid, stake_amount, fee); // Attempt to move stake from a non-existent origin hotkey add_network(netuid, 0, 0); @@ -272,9 +288,10 @@ fn test_do_move_all_stake() { let origin_hotkey = U256::from(2); let destination_hotkey = U256::from(3); let stake_amount = DefaultMinStake::<Test>::get() * 10; + let fee = DefaultMinStake::<Test>::get(); // Set up initial stake - SubtensorModule::stake_into_subnet(&origin_hotkey, &coldkey, netuid, stake_amount); + SubtensorModule::stake_into_subnet(&origin_hotkey, &coldkey, netuid, stake_amount, fee); let alpha = SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( &origin_hotkey, &coldkey, @@ -308,7 +325,7 @@ fn test_do_move_all_stake() { &coldkey, netuid ), - stake_amount, + stake_amount - 2 * fee, epsilon = stake_amount / 1000 ); }); @@ -324,9 +341,10 @@ fn test_do_move_half_stake() { let origin_hotkey = U256::from(2); let destination_hotkey = U256::from(3); let stake_amount = DefaultMinStake::<Test>::get() * 10; + let fee = DefaultMinStake::<Test>::get(); // Set up initial stake - SubtensorModule::stake_into_subnet(&origin_hotkey, &coldkey, netuid, stake_amount); + SubtensorModule::stake_into_subnet(&origin_hotkey, &coldkey, netuid, stake_amount, fee); let alpha = SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( &origin_hotkey, &coldkey, @@ -352,8 +370,8 @@ fn test_do_move_half_stake() { &coldkey, netuid ), - stake_amount / 2, - epsilon = stake_amount / 1000 + alpha / 2, + epsilon = alpha / 1000 ); assert_abs_diff_eq!( SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( @@ -361,8 +379,8 @@ fn test_do_move_half_stake() { &coldkey, netuid ), - stake_amount / 2, - epsilon = stake_amount / 1000 + alpha / 2 - fee, + epsilon = alpha / 1000 ); }); } @@ -380,9 +398,10 @@ fn test_do_move_partial_stake() { let origin_hotkey = U256::from(2); let destination_hotkey = U256::from(3); let total_stake = DefaultMinStake::<Test>::get() * 10; + let fee = DefaultMinStake::<Test>::get(); // Set up initial stake - SubtensorModule::stake_into_subnet(&origin_hotkey, &coldkey, netuid, total_stake); + SubtensorModule::stake_into_subnet(&origin_hotkey, &coldkey, netuid, total_stake, fee); let alpha = SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( &origin_hotkey, &coldkey, @@ -416,7 +435,7 @@ fn test_do_move_partial_stake() { &coldkey, netuid ), - total_stake, + total_stake - 2 * fee, epsilon = total_stake / 1000 ); }); @@ -435,11 +454,12 @@ fn test_do_move_multiple_times() { let hotkey1 = U256::from(2); let hotkey2 = U256::from(3); let initial_stake = DefaultMinStake::<Test>::get() * 10; + let fee = DefaultMinStake::<Test>::get(); // 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); + SubtensorModule::stake_into_subnet(&hotkey1, &coldkey, netuid, initial_stake, fee); // Move stake multiple times for _ in 0..3 { @@ -470,7 +490,7 @@ fn test_do_move_multiple_times() { // Check final stake distribution assert_abs_diff_eq!( SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet(&hotkey1, &coldkey, netuid), - initial_stake, + initial_stake - 7 * fee, epsilon = initial_stake / 1000 ); assert_eq!( @@ -492,9 +512,10 @@ fn test_do_move_wrong_origin() { let destination_hotkey = U256::from(3); let netuid = 1; let stake_amount = 1000; + let fee = 0; // Set up initial stake - SubtensorModule::stake_into_subnet(&origin_hotkey, &coldkey, netuid, stake_amount); + SubtensorModule::stake_into_subnet(&origin_hotkey, &coldkey, netuid, stake_amount, fee); let alpha = SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( &origin_hotkey, &coldkey, @@ -549,10 +570,11 @@ fn test_do_move_same_hotkey() { let coldkey = U256::from(1); let hotkey = U256::from(2); let stake_amount = DefaultMinStake::<Test>::get() * 10; + let fee = DefaultMinStake::<Test>::get(); // Set up initial stake SubtensorModule::create_account_if_non_existent(&coldkey, &hotkey); - SubtensorModule::stake_into_subnet(&hotkey, &coldkey, netuid, stake_amount); + SubtensorModule::stake_into_subnet(&hotkey, &coldkey, netuid, stake_amount, fee); let alpha = SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet(&hotkey, &coldkey, netuid); @@ -569,8 +591,8 @@ fn test_do_move_same_hotkey() { // Check that stake remains unchanged assert_abs_diff_eq!( SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet(&hotkey, &coldkey, netuid), - alpha, - epsilon = 5 + alpha - fee, + epsilon = alpha / 1000 ); }); } @@ -588,11 +610,12 @@ fn test_do_move_event_emission() { let origin_hotkey = U256::from(2); let destination_hotkey = U256::from(3); let stake_amount = DefaultMinStake::<Test>::get() * 10; + let fee = DefaultMinStake::<Test>::get(); // 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); + SubtensorModule::stake_into_subnet(&origin_hotkey, &coldkey, netuid, stake_amount, 0); // use 0 fee for precision let alpha = SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( &origin_hotkey, &coldkey, @@ -618,7 +641,7 @@ fn test_do_move_event_emission() { netuid, destination_hotkey, netuid, - 19999999, // Should be TAO equivalent + stake_amount - fee - 1, // Should be TAO equivalent ) .into(), ); @@ -639,9 +662,16 @@ fn test_do_move_storage_updates() { let origin_hotkey = U256::from(2); let destination_hotkey = U256::from(3); let stake_amount = DefaultMinStake::<Test>::get() * 10; + let fee = DefaultMinStake::<Test>::get(); // Set up initial stake - SubtensorModule::stake_into_subnet(&origin_hotkey, &coldkey, origin_netuid, stake_amount); + SubtensorModule::stake_into_subnet( + &origin_hotkey, + &coldkey, + origin_netuid, + stake_amount, + fee, + ); // Move stake SubtensorModule::create_account_if_non_existent(&coldkey, &origin_hotkey); @@ -676,8 +706,8 @@ fn test_do_move_storage_updates() { &coldkey, destination_netuid ), - alpha, - epsilon = 5 + alpha - fee, + epsilon = alpha / 1000 ); }); } @@ -695,11 +725,12 @@ fn test_do_move_max_values() { let destination_hotkey = U256::from(3); let max_stake = u64::MAX; let netuid: u16 = add_dynamic_network(&subnet_owner_hotkey, &subnet_owner_coldkey); + let fee = 0; // 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); + SubtensorModule::stake_into_subnet(&origin_hotkey, &coldkey, netuid, max_stake, fee); let alpha = SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( &origin_hotkey, &coldkey, @@ -744,19 +775,20 @@ fn test_moving_too_little_fails() { let hotkey_account_id = U256::from(533453); let coldkey_account_id = U256::from(55453); let amount = DefaultMinStake::<Test>::get(); + let fee = DefaultMinStake::<Test>::get(); //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); + SubtensorModule::add_balance_to_coldkey_account(&coldkey_account_id, amount + fee); assert_ok!(SubtensorModule::add_stake( RuntimeOrigin::signed(coldkey_account_id), hotkey_account_id, netuid, - amount + amount + fee )); // Coldkey / hotkey 0 decreases take to 5%. This should fail as the minimum take is 9% diff --git a/pallets/subtensor/src/tests/senate.rs b/pallets/subtensor/src/tests/senate.rs index 00a1b897f..01dfc17d7 100644 --- a/pallets/subtensor/src/tests/senate.rs +++ b/pallets/subtensor/src/tests/senate.rs @@ -67,6 +67,7 @@ fn test_senate_join_works() { let burn_cost = 1000; let coldkey_account_id = U256::from(667); // Neighbour of the beast, har har let stake = DefaultMinStake::<Test>::get() * 100; + let fee = DefaultMinStake::<Test>::get(); //add network SubtensorModule::set_burn(netuid, burn_cost); @@ -112,12 +113,12 @@ fn test_senate_join_works() { &staker_coldkey, netuid ), - stake, + stake - fee, epsilon = 10 ); assert_abs_diff_eq!( SubtensorModule::get_stake_for_hotkey_on_subnet(&hotkey_account_id, netuid), - stake, + stake - fee, epsilon = 10 ); @@ -140,6 +141,7 @@ fn test_senate_vote_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 fee = DefaultMinStake::<Test>::get(); //add network SubtensorModule::set_burn(netuid, burn_cost); @@ -185,12 +187,12 @@ fn test_senate_vote_works() { &staker_coldkey, netuid ), - stake, + stake - fee, epsilon = stake / 1000 ); assert_abs_diff_eq!( SubtensorModule::get_stake_for_hotkey_on_subnet(&hotkey_account_id, netuid), - stake, + stake - fee, epsilon = stake / 1000 ); @@ -313,6 +315,7 @@ fn test_senate_leave_works() { let burn_cost = 1000; let coldkey_account_id = U256::from(667); // Neighbour of the beast, har har let stake = DefaultMinStake::<Test>::get() * 10; + let fee = DefaultMinStake::<Test>::get(); //add network SubtensorModule::set_burn(netuid, burn_cost); @@ -357,12 +360,12 @@ fn test_senate_leave_works() { &staker_coldkey, netuid ), - stake, + stake - fee, epsilon = stake / 1000 ); assert_abs_diff_eq!( SubtensorModule::get_stake_for_hotkey_on_subnet(&hotkey_account_id, netuid), - stake, + stake - fee, epsilon = stake / 1000 ); @@ -387,6 +390,7 @@ fn test_senate_leave_vote_removal() { let coldkey_account_id = U256::from(667); // Neighbour of the beast, har har let coldkey_origin = <<Test as Config>::RuntimeOrigin>::signed(coldkey_account_id); let stake = DefaultMinStake::<Test>::get() * 10; + let fee = DefaultMinStake::<Test>::get(); //add network SubtensorModule::set_burn(netuid, burn_cost); @@ -431,12 +435,12 @@ fn test_senate_leave_vote_removal() { &staker_coldkey, netuid ), - stake, + stake - fee, epsilon = 10 ); assert_abs_diff_eq!( SubtensorModule::get_stake_for_hotkey_on_subnet(&hotkey_account_id, netuid), - stake, + stake - fee, epsilon = 10 ); @@ -528,6 +532,7 @@ fn test_senate_not_leave_when_stake_removed() { 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 fee = DefaultMinStake::<Test>::get(); //add network SubtensorModule::set_burn(netuid, burn_cost); @@ -573,12 +578,12 @@ fn test_senate_not_leave_when_stake_removed() { &staker_coldkey, netuid ), - stake_amount, + stake_amount - fee, epsilon = stake_amount / 1000 ); assert_abs_diff_eq!( SubtensorModule::get_stake_for_hotkey_on_subnet(&hotkey_account_id, netuid), - stake_amount, + stake_amount - fee, epsilon = stake_amount / 1000 ); @@ -686,6 +691,7 @@ fn test_adjust_senate_events() { let burn_cost = 1000; let coldkey_account_id = U256::from(667); let root_netuid = SubtensorModule::get_root_netuid(); + let fee = DefaultMinStake::<Test>::get(); let max_senate_size: u16 = SenateMaxMembers::get() as u16; let stake_threshold: u64 = DefaultMinStake::<Test>::get(); // Give this much to every senator @@ -806,7 +812,7 @@ fn test_adjust_senate_events() { &coldkey_account_id, root_netuid ), - stake, + stake - fee, epsilon = stake / 1000 ); assert_abs_diff_eq!( @@ -814,7 +820,7 @@ fn test_adjust_senate_events() { &replacement_hotkey_account_id, root_netuid ), - stake, + stake - fee, epsilon = stake / 1000 ); diff --git a/pallets/subtensor/src/tests/staking.rs b/pallets/subtensor/src/tests/staking.rs index 882184811..ef5ac5f4a 100644 --- a/pallets/subtensor/src/tests/staking.rs +++ b/pallets/subtensor/src/tests/staking.rs @@ -42,6 +42,7 @@ fn test_add_stake_ok_no_emission() { let hotkey_account_id = U256::from(533453); let coldkey_account_id = U256::from(55453); let amount = DefaultMinStake::<Test>::get() * 10; + let fee = DefaultMinStake::<Test>::get(); //add network let netuid: u16 = add_dynamic_network(&hotkey_account_id, &coldkey_account_id); @@ -69,7 +70,7 @@ fn test_add_stake_ok_no_emission() { // Check if stake has increased assert_abs_diff_eq!( SubtensorModule::get_total_stake_for_hotkey(&hotkey_account_id), - amount, + amount - fee, epsilon = amount / 1000, ); @@ -78,7 +79,6 @@ fn test_add_stake_ok_no_emission() { // Check if total stake has increased accordingly. assert_eq!(SubtensorModule::get_total_stake(), amount); - assert_abs_diff_eq!(SubtensorModule::get_total_stake(), amount, epsilon = 1,); }); } @@ -350,7 +350,8 @@ fn test_remove_stake_ok_no_emission() { let subnet_owner_hotkey = U256::from(2); let coldkey_account_id = U256::from(4343); let hotkey_account_id = U256::from(4968585); - let amount = 10000; + let amount = DefaultMinStake::<Test>::get() * 10; + let fee = DefaultMinStake::<Test>::get(); let netuid: u16 = add_dynamic_network(&subnet_owner_hotkey, &subnet_owner_coldkey); register_ok_neuron(netuid, hotkey_account_id, coldkey_account_id, 192213123); @@ -379,12 +380,12 @@ fn test_remove_stake_ok_no_emission() { )); // we do not expect the exact amount due to slippage - assert!(SubtensorModule::get_coldkey_balance(&coldkey_account_id) > amount / 10 * 9,); + assert!(SubtensorModule::get_coldkey_balance(&coldkey_account_id) > amount / 10 * 9 - fee); assert_eq!( SubtensorModule::get_total_stake_for_hotkey(&hotkey_account_id), 0 ); - assert_eq!(SubtensorModule::get_total_stake(), 0); + assert_eq!(SubtensorModule::get_total_stake(), fee); }); } @@ -498,6 +499,7 @@ fn test_remove_stake_no_enough_stake() { #[test] fn test_remove_stake_total_balance_no_change() { // When we remove stake, the total balance of the coldkey account should not change + // (except for staking fees) // this is because the stake should be part of the coldkey account balance (reserved/locked) // then the removed stake just becomes free balance new_test_ext(1).execute_with(|| { @@ -505,7 +507,8 @@ fn test_remove_stake_total_balance_no_change() { let subnet_owner_hotkey = U256::from(2); let hotkey_account_id = U256::from(571337); let coldkey_account_id = U256::from(71337); - let amount = 10_000; + let amount = DefaultMinStake::<Test>::get() * 10; + let fee = DefaultMinStake::<Test>::get(); let netuid: u16 = add_dynamic_network(&subnet_owner_hotkey, &subnet_owner_coldkey); register_ok_neuron(netuid, hotkey_account_id, coldkey_account_id, 192213123); @@ -537,18 +540,18 @@ fn test_remove_stake_total_balance_no_change() { assert_abs_diff_eq!( SubtensorModule::get_coldkey_balance(&coldkey_account_id), - amount, - epsilon = 1, + amount - fee, + epsilon = amount / 1000, ); assert_eq!( SubtensorModule::get_total_stake_for_hotkey(&hotkey_account_id), 0 ); - assert_eq!(SubtensorModule::get_total_stake(), 0); + assert_eq!(SubtensorModule::get_total_stake(), fee); // Check total balance is equal to the added stake. Even after remove stake (no fee, includes reserved/locked balance) let total_balance = Balances::total_balance(&coldkey_account_id); - assert_abs_diff_eq!(total_balance, amount, epsilon = 1,); + assert_abs_diff_eq!(total_balance, amount - fee, epsilon = amount / 1000); }); } @@ -564,6 +567,7 @@ fn test_remove_stake_total_issuance_no_change() { let coldkey_account_id = U256::from(81337); let amount = DefaultMinStake::<Test>::get() * 10; let netuid: u16 = add_dynamic_network(&subnet_owner_hotkey, &subnet_owner_coldkey); + let fee = DefaultMinStake::<Test>::get(); register_ok_neuron(netuid, hotkey_account_id, coldkey_account_id, 192213123); // Give it some $$$ in his coldkey balance @@ -610,14 +614,18 @@ fn test_remove_stake_total_issuance_no_change() { assert_abs_diff_eq!( SubtensorModule::get_coldkey_balance(&coldkey_account_id), - amount, - epsilon = 1, + amount - fee * 2, + epsilon = amount / 1000, ); assert_eq!( SubtensorModule::get_total_stake_for_hotkey(&hotkey_account_id), 0 ); - assert_abs_diff_eq!(SubtensorModule::get_total_stake(), 0, epsilon = 1,); + assert_abs_diff_eq!( + SubtensorModule::get_total_stake(), + fee * 2, + epsilon = fee / 1000 + ); // Check if total issuance is equal to the added stake, even after remove stake (no fee, includes reserved/locked balance) assert_abs_diff_eq!( @@ -625,10 +633,13 @@ fn test_remove_stake_total_issuance_no_change() { total_issuance_after_stake + amount, epsilon = 1, ); + + // After staking + unstaking the 2 * fee amount stays in SubnetTAO and TotalStake, + // so the total issuance should be lower by that amount assert_abs_diff_eq!( inital_total_issuance, - total_issuance_after_unstake, - epsilon = 1, + total_issuance_after_unstake + 2 * fee, + epsilon = inital_total_issuance / 10000, ); }); } @@ -1108,6 +1119,8 @@ fn test_clear_small_nominations() { let cold2 = U256::from(4); let netuid: u16 = add_dynamic_network(&subnet_owner_hotkey, &subnet_owner_coldkey); let amount = DefaultMinStake::<Test>::get() * 10; + let fee: u64 = DefaultMinStake::<Test>::get(); + let init_balance = amount + fee + ExistentialDeposit::get(); // Register hot1. register_ok_neuron(netuid, hot1, cold1, 0); @@ -1120,80 +1133,84 @@ 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, amount); + SubtensorModule::add_balance_to_coldkey_account(&cold1, init_balance); assert_ok!(SubtensorModule::add_stake( RuntimeOrigin::signed(cold1), hot1, netuid, - amount + amount + fee )); assert_ok!(SubtensorModule::remove_stake( RuntimeOrigin::signed(cold1), hot1, netuid, - SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet(&hot1, &cold1, netuid) - 1 + SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet(&hot1, &cold1, netuid) + - 100 )); assert_eq!( SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet(&hot1, &cold1, netuid), - 1 + 100 ); // Add stake cold2 --> hot1 (is delegation.) - SubtensorModule::add_balance_to_coldkey_account(&cold2, amount); + SubtensorModule::add_balance_to_coldkey_account(&cold2, init_balance); assert_ok!(SubtensorModule::add_stake( RuntimeOrigin::signed(cold2), hot1, netuid, - amount + amount + fee )); assert_ok!(SubtensorModule::remove_stake( RuntimeOrigin::signed(cold2), hot1, netuid, - SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet(&hot1, &cold2, netuid) - 1 + SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet(&hot1, &cold2, netuid) + - 100 )); assert_eq!( SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet(&hot1, &cold2, netuid), - 1 + 100 ); // Add stake cold1 --> hot2 (non delegation.) - SubtensorModule::add_balance_to_coldkey_account(&cold1, amount); + SubtensorModule::add_balance_to_coldkey_account(&cold1, init_balance); assert_ok!(SubtensorModule::add_stake( RuntimeOrigin::signed(cold1), hot2, netuid, - amount + amount + fee )); assert_ok!(SubtensorModule::remove_stake( RuntimeOrigin::signed(cold1), hot2, netuid, - SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet(&hot2, &cold1, netuid) - 1 + SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet(&hot2, &cold1, netuid) + - 100 )); assert_eq!( SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet(&hot2, &cold1, netuid), - 1 + 100 ); let balance1_before_cleaning = Balances::free_balance(cold1); // Add stake cold2 --> hot2 (is delegation.) - SubtensorModule::add_balance_to_coldkey_account(&cold2, amount); + SubtensorModule::add_balance_to_coldkey_account(&cold2, init_balance); assert_ok!(SubtensorModule::add_stake( RuntimeOrigin::signed(cold2), hot2, netuid, - amount + amount + fee )); assert_ok!(SubtensorModule::remove_stake( RuntimeOrigin::signed(cold2), hot2, netuid, - SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet(&hot2, &cold2, netuid) - 1 + SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet(&hot2, &cold2, netuid) + - 100 )); assert_eq!( SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet(&hot2, &cold2, netuid), - 1 + 100 ); let balance2_before_cleaning = Balances::free_balance(cold2); @@ -1203,19 +1220,19 @@ fn test_clear_small_nominations() { SubtensorModule::clear_small_nominations(); assert_eq!( SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet(&hot1, &cold1, netuid), - 1 + 100 ); assert_eq!( SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet(&hot2, &cold1, netuid), - 1 + 100 ); assert_eq!( SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet(&hot1, &cold2, netuid), - 1 + 100 ); assert_eq!( SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet(&hot2, &cold2, netuid), - 1 + 100 ); // Set min nomination to 10 @@ -1224,13 +1241,13 @@ fn test_clear_small_nominations() { let total_hot1_stake_before = TotalHotkeyAlpha::<Test>::get(hot1, netuid); let total_hot2_stake_before = TotalHotkeyAlpha::<Test>::get(hot2, netuid); let total_stake_before = TotalStake::<Test>::get(); - SubtensorModule::set_nominator_min_required_stake(10); + SubtensorModule::set_nominator_min_required_stake(1000); // Run clear all small nominations (removes delegations under 10) SubtensorModule::clear_small_nominations(); assert_eq!( SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet(&hot1, &cold1, netuid), - 1 + 100 ); assert_eq!( SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet(&hot2, &cold1, netuid), @@ -1242,24 +1259,26 @@ fn test_clear_small_nominations() { ); assert_eq!( SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet(&hot2, &cold2, netuid), - 1 + 100 ); // Balances have been added back into accounts. 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!(balance1_before_cleaning + 100, balance1_after_cleaning); + assert_eq!(balance2_before_cleaning + 100, balance2_after_cleaning); - assert_eq!( + assert_abs_diff_eq!( TotalHotkeyAlpha::<Test>::get(hot2, netuid), - total_hot2_stake_before - 1 + total_hot2_stake_before - 100, + epsilon = 1 ); - assert_eq!( + assert_abs_diff_eq!( TotalHotkeyAlpha::<Test>::get(hot1, netuid), - total_hot1_stake_before - 1 + total_hot1_stake_before - 100, + epsilon = 1 ); - assert_eq!(TotalStake::<Test>::get(), total_stake_before - 2); + assert_eq!(TotalStake::<Test>::get(), total_stake_before - 200); }); } @@ -1590,6 +1609,7 @@ fn test_get_total_delegated_stake_after_unstaking() { let unstake_amount = DefaultMinStake::<Test>::get() * 5; let existential_deposit = ExistentialDeposit::get(); let netuid = add_dynamic_network(&subnet_owner_hotkey, &subnet_owner_coldkey); + let fee = DefaultMinStake::<Test>::get(); register_ok_neuron(netuid, delegate_hotkey, delegate_coldkey, 0); @@ -1607,12 +1627,12 @@ fn test_get_total_delegated_stake_after_unstaking() { // Check initial delegated stake assert_abs_diff_eq!( SubtensorModule::get_total_stake_for_coldkey(&delegator), - initial_stake - existential_deposit, + initial_stake - existential_deposit - fee, epsilon = initial_stake / 1000, ); assert_abs_diff_eq!( SubtensorModule::get_total_stake_for_hotkey(&delegate_hotkey), - initial_stake - existential_deposit, + initial_stake - existential_deposit - fee, epsilon = initial_stake / 1000, ); @@ -1625,7 +1645,7 @@ fn test_get_total_delegated_stake_after_unstaking() { )); // Calculate the expected delegated stake - let expected_delegated_stake = initial_stake - unstake_amount - existential_deposit; + let expected_delegated_stake = initial_stake - unstake_amount - existential_deposit - fee; // Debug prints log::debug!("Initial stake: {}", initial_stake); @@ -1677,6 +1697,7 @@ fn test_get_total_delegated_stake_single_delegator() { let stake_amount = DefaultMinStake::<Test>::get() * 10 - 1; let existential_deposit = ExistentialDeposit::get(); let netuid = add_dynamic_network(&subnet_owner_hotkey, &subnet_owner_coldkey); + let fee = DefaultMinStake::<Test>::get(); register_ok_neuron(netuid, delegate_hotkey, delegate_coldkey, 0); @@ -1705,7 +1726,7 @@ fn test_get_total_delegated_stake_single_delegator() { ); // Calculate expected delegated stake - let expected_delegated_stake = stake_amount - existential_deposit; + let expected_delegated_stake = stake_amount - existential_deposit - fee; let actual_delegated_stake = SubtensorModule::get_total_stake_for_hotkey(&delegate_hotkey); let actual_delegator_stake = SubtensorModule::get_total_stake_for_coldkey(&delegator); @@ -1734,6 +1755,7 @@ fn test_get_alpha_share_stake_multiple_delegators() { let existential_deposit = 2; let stake1 = DefaultMinStake::<Test>::get() * 10; let stake2 = DefaultMinStake::<Test>::get() * 10 - 1; + let fee = DefaultMinStake::<Test>::get(); let netuid = add_dynamic_network(&subnet_owner_hotkey, &subnet_owner_coldkey); register_ok_neuron(netuid, hotkey1, coldkey1, 0); @@ -1770,7 +1792,7 @@ fn test_get_alpha_share_stake_multiple_delegators() { ); // Calculate expected total delegated stake - let expected_total_stake = stake1 + stake2 - existential_deposit * 2; + let expected_total_stake = stake1 + stake2 - existential_deposit * 2 - fee * 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); @@ -1792,6 +1814,7 @@ fn test_get_total_delegated_stake_exclude_owner_stake() { let delegator = U256::from(3); let owner_stake = DefaultMinStake::<Test>::get() * 10; let delegator_stake = DefaultMinStake::<Test>::get() * 10 - 1; + let fee = DefaultMinStake::<Test>::get(); let netuid = add_dynamic_network(&delegate_hotkey, &delegate_coldkey); @@ -1825,7 +1848,7 @@ fn test_get_total_delegated_stake_exclude_owner_stake() { ); // Check the total delegated stake (should exclude owner's stake) - let expected_delegated_stake = delegator_stake; + let expected_delegated_stake = delegator_stake - fee; let actual_delegated_stake = SubtensorModule::get_total_stake_for_coldkey(&delegate_coldkey); @@ -1969,7 +1992,8 @@ fn test_add_stake_fee_goes_to_subnet_tao() { // Calculate expected stake let expected_alpha = tao_to_stake - existential_deposit - fee; - let actual_alpha = SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet(&hotkey, &coldkey, netuid); + let actual_alpha = + SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet(&hotkey, &coldkey, netuid); let subnet_tao_after = SubnetTAO::<Test>::get(netuid); // Total subnet stake should match the sum of delegators' stakes minus existential deposits. @@ -2013,7 +2037,8 @@ fn test_remove_stake_fee_goes_to_subnet_tao() { )); // Remove all stake - let alpha_to_unstake = SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet(&hotkey, &coldkey, netuid); + let alpha_to_unstake = + SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet(&hotkey, &coldkey, netuid); assert_ok!(SubtensorModule::remove_stake( RuntimeOrigin::signed(coldkey), hotkey, @@ -2037,4 +2062,68 @@ fn test_remove_stake_fee_goes_to_subnet_tao() { epsilon = tao_to_stake / 1000 ); }); -} \ No newline at end of file +} + +#[test] +fn test_stake_below_min_validate() { + // Testing the signed extension validate function + // correctly filters the `add_stake` transaction. + + new_test_ext(0).execute_with(|| { + let subnet_owner_coldkey = U256::from(1001); + let subnet_owner_hotkey = U256::from(1002); + let hotkey = U256::from(2); + let coldkey = U256::from(3); + let amount_staked = DefaultMinStake::<Test>::get() - 1; + + let netuid = add_dynamic_network(&subnet_owner_hotkey, &subnet_owner_coldkey); + SubtensorModule::create_account_if_non_existent(&coldkey, &hotkey); + SubtensorModule::add_balance_to_coldkey_account(&coldkey, amount_staked); + + // Add stake call + let call = RuntimeCall::SubtensorModule(SubtensorCall::add_stake { + hotkey, + netuid, + amount_staked, + }); + + let info: crate::DispatchInfo = + crate::DispatchInfoOf::<<Test as frame_system::Config>::RuntimeCall>::default(); + + let extension = crate::SubtensorSignedExtension::<Test>::new(); + // Submit to the signed extension validate function + let result_no_stake = extension.validate(&coldkey, &call.clone(), &info, 10); + + // Should fail due to insufficient stake + assert_err!( + result_no_stake, + crate::TransactionValidityError::Invalid(crate::InvalidTransaction::Custom(1)) + ); + + // Increase the stake to be equal to the minimum, but leave the balance low + let amount_staked = DefaultMinStake::<Test>::get(); + let call_2 = RuntimeCall::SubtensorModule(SubtensorCall::add_stake { + hotkey, + netuid, + amount_staked, + }); + + // Submit to the signed extension validate function + let result_low_balance = extension.validate(&coldkey, &call_2.clone(), &info, 10); + + // Still doesn't pass + assert_err!( + result_low_balance, + crate::TransactionValidityError::Invalid(crate::InvalidTransaction::Custom(1)) + ); + + // Increase the coldkey balance to match the minimum + SubtensorModule::add_balance_to_coldkey_account(&coldkey, 1); + + // Submit to the signed extension validate function + let result_min_stake = extension.validate(&coldkey, &call_2.clone(), &info, 10); + + // Now the call passes + assert_ok!(result_min_stake); + }); +} diff --git a/pallets/subtensor/src/tests/swap_coldkey.rs b/pallets/subtensor/src/tests/swap_coldkey.rs index 0cbcecfaf..ae2a1649c 100644 --- a/pallets/subtensor/src/tests/swap_coldkey.rs +++ b/pallets/subtensor/src/tests/swap_coldkey.rs @@ -366,6 +366,7 @@ fn test_swap_with_max_values() { let netuid2 = 2u16; let stake = 10_000; let max_stake = 21_000_000_000_000_000; // 21 Million TAO; max possible balance. + let fee = DefaultMinStake::<Test>::get(); // Add a network add_network(netuid, 1, 0); @@ -412,7 +413,7 @@ fn test_swap_with_max_values() { ); assert_eq!( SubtensorModule::get_total_stake_for_coldkey(&new_coldkey), - max_stake + max_stake - fee ); assert_eq!( SubtensorModule::get_total_stake_for_coldkey(&old_coldkey2), @@ -420,7 +421,7 @@ fn test_swap_with_max_values() { ); assert_eq!( SubtensorModule::get_total_stake_for_coldkey(&new_coldkey2), - max_stake + max_stake - fee ); }); } @@ -434,6 +435,8 @@ fn test_swap_with_non_existent_new_coldkey() { let hotkey = U256::from(3); let stake = DefaultMinStake::<Test>::get() * 10; let netuid = 1u16; + let fee = DefaultMinStake::<Test>::get(); + add_network(netuid, 1, 0); register_ok_neuron(netuid, hotkey, old_coldkey, 1001000); // Give old coldkey some balance. @@ -459,7 +462,7 @@ fn test_swap_with_non_existent_new_coldkey() { ); assert_eq!( SubtensorModule::get_total_stake_for_coldkey(&new_coldkey), - stake + stake - fee ); }); } @@ -525,6 +528,7 @@ fn test_swap_concurrent_modifications() { let netuid: u16 = 1; let initial_stake = 1_000_000_000_000; let additional_stake = 500_000_000_000; + let fee = DefaultMinStake::<Test>::get(); // Setup initial state add_network(netuid, 1, 1); @@ -547,7 +551,7 @@ fn test_swap_concurrent_modifications() { &new_coldkey, netuid ), - initial_stake + initial_stake - fee ); // Wait some blocks @@ -576,15 +580,14 @@ fn test_swap_concurrent_modifications() { )); let eps = 500; // RAO - assert!( - (SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( + assert_abs_diff_eq!( + SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( &hotkey, &new_coldkey, netuid - ) as i64 - - (stake_before_swap + additional_stake) as i64) - .abs() - <= eps + ), + stake_before_swap + additional_stake - fee, + epsilon = eps ); assert!(!Alpha::<Test>::contains_key((hotkey, old_coldkey, netuid))); }); @@ -801,6 +804,7 @@ fn test_swap_stake_for_coldkey() { let stake_amount3 = DefaultMinStake::<Test>::get() * 30; let total_stake = stake_amount1 + stake_amount2; let mut weight = Weight::zero(); + let fee = DefaultMinStake::<Test>::get(); // Setup initial state // Add a network @@ -837,7 +841,7 @@ fn test_swap_stake_for_coldkey() { &old_coldkey, netuid ), - stake_amount1 + stake_amount1 - fee ); assert_eq!( SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( @@ -845,7 +849,7 @@ fn test_swap_stake_for_coldkey() { &old_coldkey, netuid ), - stake_amount2 + stake_amount2 - fee ); // Insert existing for same hotkey1 @@ -892,7 +896,7 @@ fn test_swap_stake_for_coldkey() { &new_coldkey, netuid ), - stake_amount1 + stake_amount3 + stake_amount1 + stake_amount3 - fee * 2 ); assert_eq!( SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( @@ -900,7 +904,7 @@ fn test_swap_stake_for_coldkey() { &new_coldkey, netuid ), - stake_amount2 + stake_amount2 - fee ); assert_eq!( SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( @@ -956,6 +960,7 @@ fn test_swap_staking_hotkeys_for_coldkey() { let stake_amount2 = DefaultMinStake::<Test>::get() * 20; let total_stake = stake_amount1 + stake_amount2; let mut weight = Weight::zero(); + let fee = DefaultMinStake::<Test>::get(); // Setup initial state // Add a network @@ -991,7 +996,7 @@ fn test_swap_staking_hotkeys_for_coldkey() { &old_coldkey, netuid ), - stake_amount1 + stake_amount1 - fee ); assert_eq!( SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( @@ -999,7 +1004,7 @@ fn test_swap_staking_hotkeys_for_coldkey() { &old_coldkey, netuid ), - stake_amount2 + stake_amount2 - fee ); // Perform the swap @@ -1027,6 +1032,7 @@ fn test_swap_delegated_stake_for_coldkey() { let stake_amount2 = DefaultMinStake::<Test>::get() * 20; let mut weight = Weight::zero(); let netuid = 1u16; + let fee = DefaultMinStake::<Test>::get(); // Setup initial state add_network(netuid, 1, 0); @@ -1081,7 +1087,7 @@ fn test_swap_delegated_stake_for_coldkey() { &new_coldkey, netuid ), - stake_amount1 + stake_amount1 - fee ); assert_eq!( SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( @@ -1089,7 +1095,7 @@ fn test_swap_delegated_stake_for_coldkey() { &new_coldkey, netuid ), - stake_amount2 + stake_amount2 - fee ); assert_eq!( SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( @@ -1583,6 +1589,7 @@ fn test_coldkey_delegations() { let netuid = 0u16; // Stake to 0 let netuid2 = 1u16; // Stake to 1 let stake = DefaultMinStake::<Test>::get() * 10; + let fee = DefaultMinStake::<Test>::get(); add_network(netuid, 13, 0); // root add_network(netuid2, 13, 0); @@ -1620,25 +1627,25 @@ fn test_coldkey_delegations() { // Verify stake was moved for the delegate assert_abs_diff_eq!( SubtensorModule::get_total_stake_for_hotkey(&delegate), - stake * 2, + stake * 2 - fee * 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), - stake * 2, + stake * 2 - fee * 2, epsilon = stake / 1000 ); assert_abs_diff_eq!( Alpha::<Test>::get((delegate, new_coldkey, netuid)).to_num::<u64>(), - stake, + stake - fee, epsilon = stake / 1000 ); assert_eq!(Alpha::<Test>::get((delegate, coldkey, netuid)), 0); assert_abs_diff_eq!( Alpha::<Test>::get((delegate, new_coldkey, netuid2)).to_num::<u64>(), - stake, + stake - fee, epsilon = stake / 1000 ); assert_eq!(Alpha::<Test>::get((delegate, coldkey, netuid2)), 0); diff --git a/pallets/subtensor/src/tests/swap_hotkey.rs b/pallets/subtensor/src/tests/swap_hotkey.rs index 0f11dfa98..d35dd6d56 100644 --- a/pallets/subtensor/src/tests/swap_hotkey.rs +++ b/pallets/subtensor/src/tests/swap_hotkey.rs @@ -66,6 +66,7 @@ fn test_swap_total_hotkey_stake() { let coldkey = U256::from(3); let amount = DefaultMinStake::<Test>::get() * 10; let mut weight = Weight::zero(); + let fee = DefaultMinStake::<Test>::get(); //add network let netuid: u16 = add_dynamic_network(&old_hotkey, &coldkey); @@ -84,7 +85,7 @@ fn test_swap_total_hotkey_stake() { // Check if stake has increased assert_abs_diff_eq!( SubtensorModule::get_total_stake_for_hotkey(&old_hotkey), - amount, + amount - fee, epsilon = amount / 1000, ); assert_abs_diff_eq!( @@ -109,7 +110,7 @@ fn test_swap_total_hotkey_stake() { ); assert_abs_diff_eq!( SubtensorModule::get_total_stake_for_hotkey(&new_hotkey), - amount, + amount - fee, epsilon = amount / 1000, ); }); diff --git a/pallets/subtensor/src/tests/weights.rs b/pallets/subtensor/src/tests/weights.rs index b71093776..10c829870 100644 --- a/pallets/subtensor/src/tests/weights.rs +++ b/pallets/subtensor/src/tests/weights.rs @@ -69,6 +69,7 @@ fn test_set_rootweights_validate() { let coldkey = U256::from(0); let hotkey: U256 = U256::from(1); // Add the hotkey field assert_ne!(hotkey, coldkey); // Ensure hotkey is NOT the same as coldkey !!! + let fee = DefaultMinStake::<Test>::get(); let who = coldkey; // The coldkey signs this transaction @@ -112,7 +113,7 @@ fn test_set_rootweights_validate() { RuntimeOrigin::signed(hotkey), hotkey, netuid, - min_stake + min_stake + fee )); // Verify stake is equal to minimum @@ -183,6 +184,7 @@ fn test_commit_weights_validate() { let coldkey = U256::from(0); let hotkey: U256 = U256::from(1); // Add the hotkey field assert_ne!(hotkey, coldkey); // Ensure hotkey is NOT the same as coldkey !!! + let fee = DefaultMinStake::<Test>::get(); let who = hotkey; // The hotkey signs this transaction @@ -226,7 +228,7 @@ fn test_commit_weights_validate() { RuntimeOrigin::signed(hotkey), hotkey, netuid, - min_stake + min_stake + fee )); // Verify stake is equal to minimum @@ -292,6 +294,7 @@ fn test_set_weights_validate() { let coldkey = U256::from(0); let hotkey: U256 = U256::from(1); assert_ne!(hotkey, coldkey); + let fee = DefaultMinStake::<Test>::get(); let who = hotkey; // The hotkey signs this transaction @@ -333,7 +336,7 @@ fn test_set_weights_validate() { RuntimeOrigin::signed(hotkey), hotkey, netuid, - min_stake + min_stake + fee )); // Verify stake is equal to minimum @@ -364,6 +367,7 @@ fn test_reveal_weights_validate() { let coldkey = U256::from(0); let hotkey: U256 = U256::from(1); // Add the hotkey field assert_ne!(hotkey, coldkey); // Ensure hotkey is NOT the same as coldkey !!! + let fee = DefaultMinStake::<Test>::get(); let who = hotkey; // The hotkey signs this transaction @@ -406,7 +410,7 @@ fn test_reveal_weights_validate() { RuntimeOrigin::signed(hotkey), hotkey, netuid, - min_stake + min_stake + fee )); // Verify stake is equal to minimum From fe28b487b90eae405411969de33a5d659746306b Mon Sep 17 00:00:00 2001 From: cuteolaf <OliverLim818@gmail.com> Date: Wed, 22 Jan 2025 06:36:14 -0800 Subject: [PATCH 033/145] fix wrong comments for some storage items --- pallets/subtensor/src/lib.rs | 48 ++++++++++++++++++------------------ 1 file changed, 24 insertions(+), 24 deletions(-) diff --git a/pallets/subtensor/src/lib.rs b/pallets/subtensor/src/lib.rs index 7f75568ba..396b7fe71 100644 --- a/pallets/subtensor/src/lib.rs +++ b/pallets/subtensor/src/lib.rs @@ -886,34 +886,34 @@ pub mod pallet { pub type TotalStake<T> = StorageValue<_, u64, ValueQuery>; #[pallet::storage] // --- ITEM ( dynamic_block ) -- block when dynamic was turned on. pub type DynamicBlock<T> = StorageValue<_, u64, ValueQuery>; - #[pallet::storage] // --- DMAP ( netuid ) --> total_volume | The total amount of TAO bought and sold since the start of the network. + #[pallet::storage] // --- MAP ( netuid ) --> total_volume | The total amount of TAO bought and sold since the start of the network. pub type SubnetVolume<T: Config> = StorageMap<_, Identity, u16, u64, ValueQuery, DefaultZeroU64<T>>; - #[pallet::storage] // --- DMAP ( netuid ) --> tao_in_subnet | Returns the amount of TAO in the subnet. + #[pallet::storage] // --- MAP ( netuid ) --> tao_in_subnet | Returns the amount of TAO in the subnet. pub type SubnetTAO<T: Config> = StorageMap<_, Identity, u16, u64, ValueQuery, DefaultZeroU64<T>>; - #[pallet::storage] // --- DMAP ( netuid ) --> alpha_in_emission | Returns the amount of alph in emission into the pool per block. + #[pallet::storage] // --- MAP ( netuid ) --> alpha_in_emission | Returns the amount of alph in emission into the pool per block. pub type SubnetAlphaInEmission<T: Config> = StorageMap<_, Identity, u16, u64, ValueQuery, DefaultZeroU64<T>>; - #[pallet::storage] // --- DMAP ( netuid ) --> alpha_out_emission | Returns the amount of alpha out emission into the network per block. + #[pallet::storage] // --- MAP ( netuid ) --> alpha_out_emission | Returns the amount of alpha out emission into the network per block. pub type SubnetAlphaOutEmission<T: Config> = StorageMap<_, Identity, u16, u64, ValueQuery, DefaultZeroU64<T>>; - #[pallet::storage] // --- DMAP ( netuid ) --> tao_in_emission | Returns the amount of tao emitted into this subent on the last block. + #[pallet::storage] // --- MAP ( netuid ) --> tao_in_emission | Returns the amount of tao emitted into this subent on the last block. pub type SubnetTaoInEmission<T: Config> = StorageMap<_, Identity, u16, u64, ValueQuery, DefaultZeroU64<T>>; - #[pallet::storage] // --- DMAP ( netuid ) --> alpha_sell_per_block | Alpha sold per block. + #[pallet::storage] // --- MAP ( netuid ) --> alpha_sell_per_block | Alpha sold per block. pub type SubnetAlphaEmissionSell<T: Config> = StorageMap<_, Identity, u16, u64, ValueQuery, DefaultZeroU64<T>>; - #[pallet::storage] // --- DMAP ( netuid ) --> total_stake_at_moment_of_subnet_registration + #[pallet::storage] // --- MAP ( netuid ) --> total_stake_at_moment_of_subnet_registration pub type TotalStakeAtDynamic<T: Config> = StorageMap<_, Identity, u16, u64, ValueQuery, DefaultZeroU64<T>>; - #[pallet::storage] // --- DMAP ( netuid ) --> alpha_supply_in_pool | Returns the amount of alpha in the subnet. + #[pallet::storage] // --- MAP ( netuid ) --> alpha_supply_in_pool | Returns the amount of alpha in the pool. pub type SubnetAlphaIn<T: Config> = StorageMap<_, Identity, u16, u64, ValueQuery, DefaultZeroU64<T>>; - #[pallet::storage] // --- DMAP ( netuid ) --> alpha_supply_in_subnet | Returns the amount of alpha in the subnet. + #[pallet::storage] // --- MAP ( netuid ) --> alpha_supply_in_subnet | Returns the amount of alpha in the subnet. pub type SubnetAlphaOut<T: Config> = StorageMap<_, Identity, u16, u64, ValueQuery, DefaultZeroU64<T>>; - #[pallet::storage] // --- DMAP ( cold ) --> Vec<hot> | Maps coldkey to hotkeys that stake to it + #[pallet::storage] // --- MAP ( cold ) --> Vec<hot> | Maps coldkey to hotkeys that stake to it pub type StakingHotkeys<T: Config> = StorageMap<_, Blake2_128Concat, T::AccountId, Vec<T::AccountId>, ValueQuery>; #[pallet::storage] // --- MAP ( cold ) --> Vec<hot> | Returns the vector of hotkeys controlled by this coldkey. @@ -970,10 +970,10 @@ pub mod pallet { U64F64, // Shares ValueQuery, >; - #[pallet::storage] // --- DMAP ( netuid ) --> token_symbol | Returns the token symbol for a subnet. + #[pallet::storage] // --- MAP ( netuid ) --> token_symbol | Returns the token symbol for a subnet. pub type TokenSymbol<T: Config> = StorageMap<_, Identity, u16, Vec<u8>, ValueQuery, DefaultUnicodeVecU8<T>>; - #[pallet::storage] // --- DMAP ( netuid ) --> subnet_name | Returns the name of the subnet. + #[pallet::storage] // --- MAP ( netuid ) --> subnet_name | Returns the name of the subnet. pub type SubnetName<T: Config> = StorageMap<_, Identity, u16, Vec<u8>, ValueQuery, DefaultUnicodeVecU8<T>>; @@ -1267,49 +1267,49 @@ pub mod pallet { pub type Keys<T: Config> = StorageDoubleMap<_, Identity, u16, Identity, u16, T::AccountId, ValueQuery, DefaultKey<T>>; #[pallet::storage] - /// --- DMAP ( netuid ) --> (hotkey, se, ve) + /// --- MAP ( netuid ) --> (hotkey, se, ve) pub type LoadedEmission<T: Config> = StorageMap<_, Identity, u16, Vec<(T::AccountId, u64, u64)>, OptionQuery>; #[pallet::storage] - /// --- DMAP ( netuid ) --> active + /// --- MAP ( netuid ) --> active pub type Active<T: Config> = StorageMap<_, Identity, u16, Vec<bool>, ValueQuery, EmptyBoolVec<T>>; #[pallet::storage] - /// --- DMAP ( netuid ) --> rank + /// --- MAP ( netuid ) --> rank pub type Rank<T: Config> = StorageMap<_, Identity, u16, Vec<u16>, ValueQuery, EmptyU16Vec<T>>; #[pallet::storage] - /// --- DMAP ( netuid ) --> trust + /// --- MAP ( netuid ) --> trust pub type Trust<T: Config> = StorageMap<_, Identity, u16, Vec<u16>, ValueQuery, EmptyU16Vec<T>>; #[pallet::storage] - /// --- DMAP ( netuid ) --> consensus + /// --- MAP ( netuid ) --> consensus pub type Consensus<T: Config> = StorageMap<_, Identity, u16, Vec<u16>, ValueQuery, EmptyU16Vec<T>>; #[pallet::storage] - /// --- DMAP ( netuid ) --> incentive + /// --- MAP ( netuid ) --> incentive pub type Incentive<T: Config> = StorageMap<_, Identity, u16, Vec<u16>, ValueQuery, EmptyU16Vec<T>>; #[pallet::storage] - /// --- DMAP ( netuid ) --> dividends + /// --- MAP ( netuid ) --> dividends pub type Dividends<T: Config> = StorageMap<_, Identity, u16, Vec<u16>, ValueQuery, EmptyU16Vec<T>>; #[pallet::storage] - /// --- DMAP ( netuid ) --> emission + /// --- MAP ( netuid ) --> emission pub type Emission<T: Config> = StorageMap<_, Identity, u16, Vec<u64>, ValueQuery, EmptyU64Vec<T>>; #[pallet::storage] - /// --- DMAP ( netuid ) --> last_update + /// --- MAP ( netuid ) --> last_update pub type LastUpdate<T: Config> = StorageMap<_, Identity, u16, Vec<u64>, ValueQuery, EmptyU64Vec<T>>; #[pallet::storage] - /// --- DMAP ( netuid ) --> validator_trust + /// --- MAP ( netuid ) --> validator_trust pub type ValidatorTrust<T: Config> = StorageMap<_, Identity, u16, Vec<u16>, ValueQuery, EmptyU16Vec<T>>; #[pallet::storage] - /// --- DMAP ( netuid ) --> pruning_scores + /// --- MAP ( netuid ) --> pruning_scores pub type PruningScores<T: Config> = StorageMap<_, Identity, u16, Vec<u16>, ValueQuery, EmptyU16Vec<T>>; #[pallet::storage] - /// --- DMAP ( netuid ) --> validator_permit + /// --- MAP ( netuid ) --> validator_permit pub type ValidatorPermit<T: Config> = StorageMap<_, Identity, u16, Vec<bool>, ValueQuery, EmptyBoolVec<T>>; #[pallet::storage] From a1d7e8713e6320665dfdb3887715922cf16ed2ca Mon Sep 17 00:00:00 2001 From: Unconst <32490803+unconst@users.noreply.github.com> Date: Wed, 22 Jan 2025 12:14:59 -0500 Subject: [PATCH 034/145] Metagraph struct (#1175) * universal subnet struct * oops typo * cargo clippy * cargo fmt * add new field --------- Co-authored-by: unconst <jake@bittensor.com> Co-authored-by: camfairchild <cameron@opentensor.dev> Co-authored-by: Cameron Fairchild <cameron@opentensor.ai> --- Cargo.lock | 184 ++++--------- pallets/subtensor/rpc/src/lib.rs | 23 ++ pallets/subtensor/runtime-api/src/lib.rs | 2 + pallets/subtensor/src/rpc_info/metagraph.rs | 287 ++++++++++++++++++++ pallets/subtensor/src/rpc_info/mod.rs | 1 + runtime/src/lib.rs | 15 + 6 files changed, 375 insertions(+), 137 deletions(-) create mode 100644 pallets/subtensor/src/rpc_info/metagraph.rs diff --git a/Cargo.lock b/Cargo.lock index 90c2fb86f..964c583e1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1550,9 +1550,9 @@ dependencies = [ [[package]] name = "crossbeam-queue" -version = "0.3.11" +version = "0.3.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df0346b5d5e76ac2fe4e327c5fd1118d6be7c51dfb18f9b7922923f287471e35" +checksum = "0f58bbc28f91df819d0aa2a2c00cd19754769c2fad90579b3592b1c9ba7a3115" dependencies = [ "crossbeam-utils", ] @@ -2187,9 +2187,9 @@ checksum = "c22d4b5885b6aa2fe5e8b9329fb8d232bf739e434e6b87347c63bdd00c120f60" dependencies = [ "crunchy", "fixed-hash", - "impl-codec 0.6.0", + "impl-codec", "impl-rlp", - "impl-serde 0.4.0", + "impl-serde", "scale-info", "tiny-keccak", ] @@ -2220,12 +2220,12 @@ checksum = "02d215cbf040552efcbe99a38372fe80ab9d00268e20012b79fcd0f073edd8ee" dependencies = [ "ethbloom", "fixed-hash", - "impl-codec 0.6.0", + "impl-codec", "impl-rlp", - "impl-serde 0.4.0", - "primitive-types 0.12.2", + "impl-serde", + "primitive-types", "scale-info", - "uint 0.9.5", + "uint", ] [[package]] @@ -2269,7 +2269,7 @@ dependencies = [ "evm-runtime", "log", "parity-scale-codec", - "primitive-types 0.12.2", + "primitive-types", "rlp", "scale-info", "serde", @@ -2283,7 +2283,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d1da6cedc5cedb4208e59467106db0d1f50db01b920920589f8e672c02fdc04f" dependencies = [ "parity-scale-codec", - "primitive-types 0.12.2", + "primitive-types", "scale-info", "serde", ] @@ -2297,7 +2297,7 @@ dependencies = [ "environmental", "evm-core", "evm-runtime", - "primitive-types 0.12.2", + "primitive-types", ] [[package]] @@ -2309,7 +2309,7 @@ dependencies = [ "auto_impl", "environmental", "evm-core", - "primitive-types 0.12.2", + "primitive-types", "sha3", ] @@ -2694,7 +2694,7 @@ version = "1.0.0-dev" source = "git+https://github.com/opentensor/frontier?rev=635bdac882#635bdac882333afed827053f31ef56ab739f7a2e" dependencies = [ "hex", - "impl-serde 0.4.0", + "impl-serde", "libsecp256k1", "log", "parity-scale-codec", @@ -3876,26 +3876,6 @@ dependencies = [ "parity-scale-codec", ] -[[package]] -name = "impl-codec" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b67aa010c1e3da95bf151bd8b4c059b2ed7e75387cdb969b4f8f2723a43f9941" -dependencies = [ - "parity-scale-codec", -] - -[[package]] -name = "impl-num-traits" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "803d15461ab0dcc56706adf266158acbc44ccf719bf7d0af30705f58b90a4b8c" -dependencies = [ - "integer-sqrt", - "num-traits", - "uint 0.10.0", -] - [[package]] name = "impl-rlp" version = "0.3.0" @@ -3914,15 +3894,6 @@ dependencies = [ "serde", ] -[[package]] -name = "impl-serde" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4a143eada6a1ec4aefa5049037a26a6d597bfd64f8c026d07b77133e02b7dd0b" -dependencies = [ - "serde", -] - [[package]] name = "impl-trait-for-tuples" version = "0.2.2" @@ -4459,7 +4430,7 @@ dependencies = [ "sha2 0.10.8", "smallvec", "thiserror", - "uint 0.9.5", + "uint", "unsigned-varint 0.7.2", "void", ] @@ -4921,7 +4892,7 @@ dependencies = [ "tokio-util", "tracing", "trust-dns-resolver", - "uint 0.9.5", + "uint", "unsigned-varint 0.8.0", "url", "webpki", @@ -6664,7 +6635,7 @@ dependencies = [ "lru 0.8.1", "parity-util-mem-derive", "parking_lot 0.12.3", - "primitive-types 0.12.2", + "primitive-types", "smallvec", "winapi", ] @@ -6906,7 +6877,7 @@ dependencies = [ "libc", "log", "polkavm-assembler", - "polkavm-common 0.9.0", + "polkavm-common", "polkavm-linux-raw", ] @@ -6928,28 +6899,13 @@ dependencies = [ "log", ] -[[package]] -name = "polkavm-common" -version = "0.17.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f0dbafef4ab6ceecb4982ac3b550df430ef4f9fdbf07c108b7d4f91a0682fce" - [[package]] name = "polkavm-derive" version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ae8c4bea6f3e11cd89bb18bcdddac10bd9a24015399bd1c485ad68a985a19606" dependencies = [ - "polkavm-derive-impl-macro 0.9.0", -] - -[[package]] -name = "polkavm-derive" -version = "0.17.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "206caf322dfc02144510ad8360ff2051e5072f0874dcab3b410f78cdd52d0ebb" -dependencies = [ - "polkavm-derive-impl-macro 0.17.0", + "polkavm-derive-impl-macro", ] [[package]] @@ -6958,19 +6914,7 @@ version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5c4fdfc49717fb9a196e74a5d28e0bc764eb394a2c803eb11133a31ac996c60c" dependencies = [ - "polkavm-common 0.9.0", - "proc-macro2", - "quote", - "syn 2.0.90", -] - -[[package]] -name = "polkavm-derive-impl" -version = "0.17.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42565aed4adbc4034612d0b17dea8db3681fb1bd1aed040d6edc5455a9f478a1" -dependencies = [ - "polkavm-common 0.17.0", + "polkavm-common", "proc-macro2", "quote", "syn 2.0.90", @@ -6982,17 +6926,7 @@ version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8ba81f7b5faac81e528eb6158a6f3c9e0bb1008e0ffa19653bc8dea925ecb429" dependencies = [ - "polkavm-derive-impl 0.9.0", - "syn 2.0.90", -] - -[[package]] -name = "polkavm-derive-impl-macro" -version = "0.17.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "86d9838e95241b0bce4fe269cdd4af96464160505840ed5a8ac8536119ba19e2" -dependencies = [ - "polkavm-derive-impl 0.17.0", + "polkavm-derive-impl", "syn 2.0.90", ] @@ -7006,7 +6940,7 @@ dependencies = [ "hashbrown 0.14.5", "log", "object 0.32.2", - "polkavm-common 0.9.0", + "polkavm-common", "regalloc2 0.9.3", "rustc-demangle", ] @@ -7143,23 +7077,11 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0b34d9fd68ae0b74a41b21c03c2f62847aa0ffea044eee893b4c140b37e244e2" dependencies = [ "fixed-hash", - "impl-codec 0.6.0", + "impl-codec", "impl-rlp", - "impl-serde 0.4.0", + "impl-serde", "scale-info", - "uint 0.9.5", -] - -[[package]] -name = "primitive-types" -version = "0.13.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d15600a7d856470b7d278b3fe0e311fe28c2526348549f8ef2ff7db3299c87f5" -dependencies = [ - "fixed-hash", - "impl-codec 0.7.0", - "impl-num-traits", - "uint 0.10.0", + "uint", ] [[package]] @@ -9405,9 +9327,9 @@ checksum = "cd0b0ec5f1c1ca621c432a25813d8d60c88abe6d3e08a3eb9cf37d97a0fe3d73" [[package]] name = "serde" -version = "1.0.215" +version = "1.0.216" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6513c1ad0b11a9376da888e3e0baa0077f1aed55c17f50e7b2397136129fb88f" +checksum = "0b9781016e935a97e8beecf0c933758c97a5520d32930e460142b4cd80c6338e" dependencies = [ "serde_derive", ] @@ -9442,9 +9364,9 @@ dependencies = [ [[package]] name = "serde_derive" -version = "1.0.215" +version = "1.0.216" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad1e866f866923f252f05c889987993144fb74e722403468a4ebd70c3cd756c0" +checksum = "46f859dbbf73865c6627ed570e78961cd3ac92407a2d117204c49232485da55e" dependencies = [ "proc-macro2", "quote", @@ -9936,7 +9858,7 @@ dependencies = [ "futures", "hash-db", "hash256-std-hasher", - "impl-serde 0.4.0", + "impl-serde", "itertools 0.11.0", "k256", "libsecp256k1", @@ -9946,7 +9868,7 @@ dependencies = [ "parity-scale-codec", "parking_lot 0.12.3", "paste", - "primitive-types 0.12.2", + "primitive-types", "rand", "scale-info", "schnorrkel", @@ -9970,7 +9892,7 @@ dependencies = [ [[package]] name = "sp-crypto-ec-utils" version = "0.10.0" -source = "git+https://github.com/paritytech/polkadot-sdk#44766de645c741fd03d159fca7f17cd6e99c33e3" +source = "git+https://github.com/paritytech/polkadot-sdk#8614dc0e055d06de4a3774ac1da0a422b33f34e2" dependencies = [ "ark-bls12-377", "ark-bls12-377-ext", @@ -10066,7 +9988,7 @@ dependencies = [ [[package]] name = "sp-debug-derive" version = "14.0.0" -source = "git+https://github.com/paritytech/polkadot-sdk#44766de645c741fd03d159fca7f17cd6e99c33e3" +source = "git+https://github.com/paritytech/polkadot-sdk#8614dc0e055d06de4a3774ac1da0a422b33f34e2" dependencies = [ "proc-macro2", "quote", @@ -10076,7 +9998,7 @@ dependencies = [ [[package]] name = "sp-externalities" version = "0.25.0" -source = "git+https://github.com/paritytech/polkadot-sdk#44766de645c741fd03d159fca7f17cd6e99c33e3" +source = "git+https://github.com/paritytech/polkadot-sdk#8614dc0e055d06de4a3774ac1da0a422b33f34e2" dependencies = [ "environmental", "parity-scale-codec", @@ -10129,7 +10051,7 @@ dependencies = [ "libsecp256k1", "log", "parity-scale-codec", - "polkavm-derive 0.9.1", + "polkavm-derive", "rustversion", "secp256k1", "sp-core", @@ -10254,13 +10176,13 @@ dependencies = [ [[package]] name = "sp-runtime-interface" version = "24.0.0" -source = "git+https://github.com/paritytech/polkadot-sdk#44766de645c741fd03d159fca7f17cd6e99c33e3" +source = "git+https://github.com/paritytech/polkadot-sdk#8614dc0e055d06de4a3774ac1da0a422b33f34e2" dependencies = [ "bytes", "impl-trait-for-tuples", "parity-scale-codec", - "polkavm-derive 0.17.1", - "primitive-types 0.13.1", + "polkavm-derive", + "primitive-types", "sp-externalities 0.25.0", "sp-runtime-interface-proc-macro 17.0.0", "sp-std 14.0.0 (git+https://github.com/paritytech/polkadot-sdk)", @@ -10278,8 +10200,8 @@ dependencies = [ "bytes", "impl-trait-for-tuples", "parity-scale-codec", - "polkavm-derive 0.9.1", - "primitive-types 0.12.2", + "polkavm-derive", + "primitive-types", "sp-externalities 0.29.0", "sp-runtime-interface-proc-macro 18.0.0", "sp-std 14.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-stable2409)", @@ -10292,7 +10214,7 @@ dependencies = [ [[package]] name = "sp-runtime-interface-proc-macro" version = "17.0.0" -source = "git+https://github.com/paritytech/polkadot-sdk#44766de645c741fd03d159fca7f17cd6e99c33e3" +source = "git+https://github.com/paritytech/polkadot-sdk#8614dc0e055d06de4a3774ac1da0a422b33f34e2" dependencies = [ "Inflector", "expander", @@ -10394,14 +10316,14 @@ source = "git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-stable [[package]] name = "sp-std" version = "14.0.0" -source = "git+https://github.com/paritytech/polkadot-sdk#44766de645c741fd03d159fca7f17cd6e99c33e3" +source = "git+https://github.com/paritytech/polkadot-sdk#8614dc0e055d06de4a3774ac1da0a422b33f34e2" [[package]] name = "sp-storage" version = "19.0.0" -source = "git+https://github.com/paritytech/polkadot-sdk#44766de645c741fd03d159fca7f17cd6e99c33e3" +source = "git+https://github.com/paritytech/polkadot-sdk#8614dc0e055d06de4a3774ac1da0a422b33f34e2" dependencies = [ - "impl-serde 0.5.0", + "impl-serde", "parity-scale-codec", "ref-cast", "serde", @@ -10413,7 +10335,7 @@ name = "sp-storage" version = "21.0.0" source = "git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-stable2409#87971b3e92721bdf10bf40b410eaae779d494ca0" dependencies = [ - "impl-serde 0.4.0", + "impl-serde", "parity-scale-codec", "ref-cast", "serde", @@ -10435,7 +10357,7 @@ dependencies = [ [[package]] name = "sp-tracing" version = "16.0.0" -source = "git+https://github.com/paritytech/polkadot-sdk#44766de645c741fd03d159fca7f17cd6e99c33e3" +source = "git+https://github.com/paritytech/polkadot-sdk#8614dc0e055d06de4a3774ac1da0a422b33f34e2" dependencies = [ "parity-scale-codec", "tracing", @@ -10505,7 +10427,7 @@ name = "sp-version" version = "37.0.0" source = "git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-stable2409#87971b3e92721bdf10bf40b410eaae779d494ca0" dependencies = [ - "impl-serde 0.4.0", + "impl-serde", "parity-scale-codec", "parity-wasm", "scale-info", @@ -10531,7 +10453,7 @@ dependencies = [ [[package]] name = "sp-wasm-interface" version = "20.0.0" -source = "git+https://github.com/paritytech/polkadot-sdk#44766de645c741fd03d159fca7f17cd6e99c33e3" +source = "git+https://github.com/paritytech/polkadot-sdk#8614dc0e055d06de4a3774ac1da0a422b33f34e2" dependencies = [ "anyhow", "impl-trait-for-tuples", @@ -11705,18 +11627,6 @@ dependencies = [ "static_assertions", ] -[[package]] -name = "uint" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "909988d098b2f738727b161a106cfc7cab00c539c2687a8836f8e565976fb53e" -dependencies = [ - "byteorder", - "crunchy", - "hex", - "static_assertions", -] - [[package]] name = "unicode-bidi" version = "0.3.17" diff --git a/pallets/subtensor/rpc/src/lib.rs b/pallets/subtensor/rpc/src/lib.rs index 5b4c7777a..cdbcebcac 100644 --- a/pallets/subtensor/rpc/src/lib.rs +++ b/pallets/subtensor/rpc/src/lib.rs @@ -55,6 +55,10 @@ pub trait SubtensorCustomApi<BlockHash> { fn get_all_dynamic_info(&self, at: Option<BlockHash>) -> RpcResult<Vec<u8>>; #[method(name = "subnetInfo_getDynamicInfo")] fn get_dynamic_info(&self, netuid: u16, at: Option<BlockHash>) -> RpcResult<Vec<u8>>; + #[method(name = "subnetInfo_getAllMetagraphs")] + fn get_all_metagraphs(&self, at: Option<BlockHash>) -> RpcResult<Vec<u8>>; + #[method(name = "subnetInfo_getMetagraph")] + fn get_metagraph(&self, netuid: u16, at: Option<BlockHash>) -> RpcResult<Vec<u8>>; #[method(name = "subnetInfo_getSubnetState")] fn get_subnet_state(&self, netuid: u16, at: Option<BlockHash>) -> RpcResult<Vec<u8>>; #[method(name = "subnetInfo_getLockCost")] @@ -223,6 +227,13 @@ where }) } + fn get_all_metagraphs(&self, at: Option<<Block as BlockT>::Hash>) -> RpcResult<Vec<u8>> { + let api = self.client.runtime_api(); + let at = at.unwrap_or_else(|| self.client.info().best_hash); + api.get_all_metagraphs(at) + .map_err(|e| Error::RuntimeError(format!("Unable to get metagraps: {:?}", e)).into()) + } + fn get_dynamic_info( &self, netuid: u16, @@ -235,6 +246,18 @@ where }) } + fn get_metagraph( + &self, + netuid: u16, + at: Option<<Block as BlockT>::Hash>, + ) -> RpcResult<Vec<u8>> { + let api = self.client.runtime_api(); + let at = at.unwrap_or_else(|| self.client.info().best_hash); + api.get_metagraph(at, netuid).map_err(|e| { + Error::RuntimeError(format!("Unable to get dynamic subnets info: {:?}", e)).into() + }) + } + fn get_subnet_state( &self, netuid: u16, diff --git a/pallets/subtensor/runtime-api/src/lib.rs b/pallets/subtensor/runtime-api/src/lib.rs index 85adada68..cdcd6ed39 100644 --- a/pallets/subtensor/runtime-api/src/lib.rs +++ b/pallets/subtensor/runtime-api/src/lib.rs @@ -25,6 +25,8 @@ sp_api::decl_runtime_apis! { fn get_subnets_info_v2() -> Vec<u8>; fn get_subnet_hyperparams(netuid: u16) -> Vec<u8>; fn get_all_dynamic_info() -> Vec<u8>; + fn get_all_metagraphs() -> Vec<u8>; + fn get_metagraph(netuid: u16) -> Vec<u8>; fn get_dynamic_info(netuid: u16) -> Vec<u8>; fn get_subnet_state(netuid: u16) -> Vec<u8>; } diff --git a/pallets/subtensor/src/rpc_info/metagraph.rs b/pallets/subtensor/src/rpc_info/metagraph.rs new file mode 100644 index 000000000..091c7e259 --- /dev/null +++ b/pallets/subtensor/src/rpc_info/metagraph.rs @@ -0,0 +1,287 @@ +use super::*; +extern crate alloc; +use crate::epoch::math::*; +use codec::Compact; +use frame_support::pallet_prelude::{Decode, Encode}; +use substrate_fixed::types::I64F64; +use subtensor_macros::freeze_struct; + +#[freeze_struct("eff674535ea437ae")] +#[derive(Decode, Encode, PartialEq, Eq, Clone, Debug)] +pub struct Metagraph<T: Config> { + // Subnet index + netuid: Compact<u16>, + + // Name and symbol + name: Vec<Compact<u8>>, // name + symbol: Vec<Compact<u8>>, // token symbol + identity: Option<SubnetIdentity>, // identity information. + network_registered_at: Compact<u64>, // block at registration + + // Keys for owner. + owner_hotkey: T::AccountId, // hotkey + owner_coldkey: T::AccountId, // coldkey. + + // Tempo terms. + block: Compact<u64>, // block at call. + tempo: Compact<u16>, // epoch tempo + last_step: Compact<u64>, // last epoch + blocks_since_last_step: Compact<u64>, // blocks since last epoch. + + // Subnet emission terms + subnet_emission: Compact<u64>, // subnet emission via stao + alpha_in: Compact<u64>, // amount of alpha in reserve + alpha_out: Compact<u64>, // amount of alpha outstanding + tao_in: Compact<u64>, // amount of tao injected per block + alpha_out_emission: Compact<u64>, // amount injected in alpha reserves per block + alpha_in_emission: Compact<u64>, // amount injected outstanding per block + tao_in_emission: Compact<u64>, // amount of tao injected per block + pending_alpha_emission: Compact<u64>, // pending alpha to be distributed + pending_root_emission: Compact<u64>, // panding tao for root divs to be distributed + + // Hparams for epoch + rho: Compact<u16>, // subnet rho param + kappa: Compact<u16>, // subnet kappa param + + // Validator params + min_allowed_weights: Compact<u16>, // min allowed weights per val + max_weights_limit: Compact<u16>, // max allowed weights per val + weights_version: Compact<u64>, // allowed weights version + weights_rate_limit: Compact<u64>, // rate limit on weights. + activity_cutoff: Compact<u16>, // validator weights cut off period in blocks + max_validators: Compact<u16>, // max allowed validators. + + // Registration + num_uids: Compact<u16>, + max_uids: Compact<u16>, + burn: Compact<u64>, // current burn cost.. + difficulty: Compact<u64>, // current difficulty. + registration_allowed: bool, // allows registrations. + pow_registration_allowed: bool, // pow registration enabled. + immunity_period: Compact<u16>, // subnet miner immunity period + min_difficulty: Compact<u64>, // min pow difficulty + max_difficulty: Compact<u64>, // max pow difficulty + min_burn: Compact<u64>, // min tao burn + max_burn: Compact<u64>, // max tao burn + adjustment_alpha: Compact<u64>, // adjustment speed for registration params. + adjustment_interval: Compact<u16>, // pow and burn adjustment interval + target_regs_per_interval: Compact<u16>, // target registrations per interval + max_regs_per_block: Compact<u16>, // max registrations per block. + serving_rate_limit: Compact<u64>, // axon serving rate limit + + // CR + commit_reveal_weights_enabled: bool, // Is CR enabled. + commit_reveal_period: Compact<u64>, // Commit reveal interval + + // Bonds + liquid_alpha_enabled: bool, // Bonds liquid enabled. + alpha_high: Compact<u16>, // Alpha param high + alpha_low: Compact<u16>, // Alpha param low + bonds_moving_avg: Compact<u64>, // Bonds moving avg + + // Metagraph info. + hotkeys: Vec<T::AccountId>, // hotkey per UID + coldkeys: Vec<T::AccountId>, // coldkey per UID + identities: Vec<ChainIdentityOf>, // coldkeys identities + axons: Vec<AxonInfo>, // UID axons. + active: Vec<bool>, // Avtive per UID + validator_permit: Vec<bool>, // Val permit per UID + pruning_score: Vec<Compact<u16>>, // Pruning per UID + last_update: Vec<Compact<u64>>, // Last update per UID + emission: Vec<Compact<u64>>, // Emission per UID + dividends: Vec<Compact<u16>>, // Dividends per UID + incentives: Vec<Compact<u16>>, // Mining incentives per UID + consensus: Vec<Compact<u16>>, // Consensus per UID + trust: Vec<Compact<u16>>, // Trust per UID + rank: Vec<Compact<u16>>, // Rank per UID + block_at_registration: Vec<Compact<u64>>, // Reg block per UID + alpha_stake: Vec<Compact<u64>>, // Alpha staked per UID + tao_stake: Vec<Compact<u64>>, // TAO staked per UID + total_stake: Vec<Compact<u64>>, // Total stake per UID + + // Dividend break down. + tao_dividends_per_hotkey: Vec<(T::AccountId, Compact<u64>)>, // List of dividend payouts in tao via root. + alpha_dividends_per_hotkey: Vec<(T::AccountId, Compact<u64>)>, // List of dividend payout in alpha via subnet. +} + +impl<T: Config> Pallet<T> { + pub fn get_metagraph(netuid: u16) -> Option<Metagraph<T>> { + if !Self::if_subnet_exist(netuid) { + return None; + } + + let n: u16 = Self::get_subnetwork_n(netuid); + let mut hotkeys: Vec<T::AccountId> = vec![]; + let mut coldkeys: Vec<T::AccountId> = vec![]; + let mut block_at_registration: Vec<Compact<u64>> = vec![]; + let mut identities: Vec<ChainIdentityOf> = vec![]; + let mut axons: Vec<AxonInfo> = vec![]; + for uid in 0..n { + let hotkey = Keys::<T>::get(netuid, uid); + let coldkey = Owner::<T>::get(hotkey.clone()); + hotkeys.push(hotkey.clone()); + coldkeys.push(coldkey.clone()); + block_at_registration.push(BlockAtRegistration::<T>::get(netuid, uid).into()); + identities.push(Identities::<T>::get(coldkey.clone())?); + axons.push(Self::get_axon_info(netuid, &hotkey)); + } + let mut tao_dividends_per_hotkey: Vec<(T::AccountId, Compact<u64>)> = vec![]; + let mut alpha_dividends_per_hotkey: Vec<(T::AccountId, Compact<u64>)> = vec![]; + for hotkey in hotkeys.clone() { + let tao_divs = TaoDividendsPerSubnet::<T>::get(netuid, hotkey.clone()); + let alpha_divs = AlphaDividendsPerSubnet::<T>::get(netuid, hotkey.clone()); + tao_dividends_per_hotkey.push((hotkey.clone(), tao_divs.into())); + alpha_dividends_per_hotkey.push((hotkey.clone(), alpha_divs.into())); + } + let current_block: u64 = Pallet::<T>::get_current_block_as_u64(); + let last_step = LastMechansimStepBlock::<T>::get(netuid); + let blocks_since_last_step: u64 = current_block.saturating_sub(last_step); + let (total_stake_fl, alpha_stake_fl, tao_stake_fl): ( + Vec<I64F64>, + Vec<I64F64>, + Vec<I64F64>, + ) = Self::get_stake_weights_for_network(netuid); + Some(Metagraph { + // Subnet index + netuid: netuid.into(), // subnet index. + + // Name and symbol + name: Self::get_name_for_subnet(netuid) + .into_iter() + .map(Compact) + .collect(), // Name + symbol: Self::get_symbol_for_subnet(netuid) + .into_iter() + .map(Compact) + .collect(), // Symbol. + identity: SubnetIdentities::<T>::get(netuid), // identity information. + network_registered_at: NetworkRegisteredAt::<T>::get(netuid).into(), // block at registration + + // Keys for owner. + owner_hotkey: SubnetOwnerHotkey::<T>::get(netuid), // Owner hotkey + owner_coldkey: SubnetOwner::<T>::get(netuid), // Owner Coldkey + + // Tempo terms. + block: current_block.into(), // Block at call. + tempo: Self::get_tempo(netuid).into(), // epoch tempo + last_step: LastMechansimStepBlock::<T>::get(netuid).into(), // last epoch + blocks_since_last_step: blocks_since_last_step.into(), // blocks since last epoch. + + // Subnet emission terms + subnet_emission: EmissionValues::<T>::get(netuid).into(), // subnet emission via stao + alpha_in: SubnetAlphaIn::<T>::get(netuid).into(), // amount of alpha in reserve + alpha_out: SubnetAlphaOut::<T>::get(netuid).into(), // amount of alpha outstanding + tao_in: SubnetTAO::<T>::get(netuid).into(), // amount of tao injected per block + alpha_out_emission: SubnetAlphaOutEmission::<T>::get(netuid).into(), // amount injected in alpha reserves per block + alpha_in_emission: SubnetAlphaInEmission::<T>::get(netuid).into(), // amount injected outstanding per block + tao_in_emission: SubnetTaoInEmission::<T>::get(netuid).into(), // amount of tao injected per block + pending_alpha_emission: PendingEmission::<T>::get(netuid).into(), // pending alpha to be distributed + pending_root_emission: PendingRootDivs::<T>::get(netuid).into(), // panding tao for root divs to be distributed + + // Hparams for epoch + rho: Self::get_rho(netuid).into(), // subnet rho param + kappa: Self::get_kappa(netuid).into(), // subnet kappa param + + // Validator params + min_allowed_weights: Self::get_min_allowed_weights(netuid).into(), // min allowed weights per val + max_weights_limit: Self::get_max_weight_limit(netuid).into(), // max allowed weight + weights_version: Self::get_weights_version_key(netuid).into(), // allowed weights version + weights_rate_limit: Self::get_weights_set_rate_limit(netuid).into(), // rate limit on weights. + activity_cutoff: Self::get_activity_cutoff(netuid).into(), // validator weights cut off period in blocks + max_validators: Self::get_max_allowed_validators(netuid).into(), // max allowed validators. + + // Registration + num_uids: Self::get_subnetwork_n(netuid).into(), + max_uids: Self::get_max_allowed_uids(netuid).into(), + registration_allowed: Self::get_network_registration_allowed(netuid), // allows registrations. + pow_registration_allowed: Self::get_network_pow_registration_allowed(netuid), // allows pow registrations. + difficulty: Self::get_difficulty_as_u64(netuid).into(), // current difficulty. + burn: Self::get_burn_as_u64(netuid).into(), + immunity_period: Self::get_immunity_period(netuid).into(), // subnet miner immunity period + min_difficulty: Self::get_min_difficulty(netuid).into(), // min pow difficulty + max_difficulty: Self::get_max_difficulty(netuid).into(), // max pow difficulty + min_burn: Self::get_min_burn_as_u64(netuid).into(), // min tao burn + max_burn: Self::get_max_burn_as_u64(netuid).into(), // max tao burn + adjustment_alpha: Self::get_adjustment_alpha(netuid).into(), // adjustment speed for registration params. + adjustment_interval: Self::get_adjustment_interval(netuid).into(), // pow and burn adjustment interval + target_regs_per_interval: Self::get_target_registrations_per_interval(netuid).into(), // target registrations per interval + max_regs_per_block: Self::get_max_registrations_per_block(netuid).into(), // max registrations per block. + serving_rate_limit: Self::get_serving_rate_limit(netuid).into(), // axon serving rate limit + + // CR + commit_reveal_weights_enabled: Self::get_commit_reveal_weights_enabled(netuid), // Is CR enabled. + commit_reveal_period: Self::get_reveal_period(netuid).into(), // Commit reveal interval + + // Bonds + liquid_alpha_enabled: Self::get_liquid_alpha_enabled(netuid), // Bonds liquid enabled. + alpha_high: Self::get_alpha_values(netuid).1.into(), // Alpha param high + alpha_low: Self::get_alpha_values(netuid).0.into(), // Alpha param low + bonds_moving_avg: Self::get_bonds_moving_average(netuid).into(), // Bonds moving avg + + // Metagraph info. + hotkeys, // hotkey per UID + coldkeys, // coldkey per UID + axons, // Axon information per UID. + identities, + active: Active::<T>::get(netuid), // Avtive per UID + validator_permit: ValidatorPermit::<T>::get(netuid), // Val permit per UID + pruning_score: PruningScores::<T>::get(netuid) + .into_iter() + .map(Compact::from) + .collect(), // Pruning per UID + last_update: LastUpdate::<T>::get(netuid) + .into_iter() + .map(Compact::from) + .collect(), // Last update per UID + emission: Emission::<T>::get(netuid) + .into_iter() + .map(Compact::from) + .collect(), // Emission per UID + dividends: Dividends::<T>::get(netuid) + .into_iter() + .map(Compact::from) + .collect(), // Dividends per UID + incentives: Incentive::<T>::get(netuid) + .into_iter() + .map(Compact::from) + .collect(), // Mining incentives per UID + consensus: Consensus::<T>::get(netuid) + .into_iter() + .map(Compact::from) + .collect(), // Consensus per UID + trust: Trust::<T>::get(netuid) + .into_iter() + .map(Compact::from) + .collect(), // Trust per UID + rank: Rank::<T>::get(netuid) + .into_iter() + .map(Compact::from) + .collect(), // Rank per UID + block_at_registration, // Reg block per UID + alpha_stake: alpha_stake_fl + .iter() + .map(|xi| Compact::from(fixed64_to_u64(*xi))) + .collect::<Vec<Compact<u64>>>(), // Alpha staked per UID + tao_stake: tao_stake_fl + .iter() + .map(|xi| Compact::from(fixed64_to_u64(*xi))) + .collect::<Vec<Compact<u64>>>(), // TAO staked per UID + total_stake: total_stake_fl + .iter() + .map(|xi| Compact::from(fixed64_to_u64(*xi))) + .collect::<Vec<Compact<u64>>>(), // Total stake per UID + + // Dividend break down. + tao_dividends_per_hotkey, + alpha_dividends_per_hotkey, + }) + } + pub fn get_all_metagraphs() -> Vec<Option<Metagraph<T>>> { + let netuids: Vec<u16> = Self::get_all_subnet_netuids(); + let mut metagraphs = Vec::<Option<Metagraph<T>>>::new(); + for netuid in netuids.clone().iter() { + metagraphs.push(Self::get_metagraph(*netuid)); + } + metagraphs + } +} diff --git a/pallets/subtensor/src/rpc_info/mod.rs b/pallets/subtensor/src/rpc_info/mod.rs index 4c224050e..13b2e9cbb 100644 --- a/pallets/subtensor/src/rpc_info/mod.rs +++ b/pallets/subtensor/src/rpc_info/mod.rs @@ -1,6 +1,7 @@ use super::*; pub mod delegate_info; pub mod dynamic_info; +pub mod metagraph; pub mod neuron_info; pub mod show_subnet; pub mod stake_info; diff --git a/runtime/src/lib.rs b/runtime/src/lib.rs index 128d2b8f3..5c3688a51 100644 --- a/runtime/src/lib.rs +++ b/runtime/src/lib.rs @@ -2112,6 +2112,16 @@ impl_runtime_apis! { } } + fn get_metagraph(netuid: u16) -> Vec<u8> { + let _result = SubtensorModule::get_metagraph(netuid); + if _result.is_some() { + let result = _result.expect("Could not get Metagraph."); + result.encode() + } else { + vec![] + } + } + fn get_subnet_state(netuid: u16) -> Vec<u8> { let _result = SubtensorModule::get_subnet_state(netuid); if _result.is_some() { @@ -2122,6 +2132,11 @@ impl_runtime_apis! { } } + fn get_all_metagraphs() -> Vec<u8> { + let result = SubtensorModule::get_all_metagraphs(); + result.encode() + } + fn get_all_dynamic_info() -> Vec<u8> { let result = SubtensorModule::get_all_dynamic_info(); result.encode() From af8cd5ec44946ae4e9e6820c89c90162f27cb2cd Mon Sep 17 00:00:00 2001 From: Greg Zaitsev <gregz@opentensor.dev> Date: Wed, 22 Jan 2025 13:24:05 -0500 Subject: [PATCH 035/145] Fix tests and transfer stake after merging devnet-ready --- pallets/subtensor/src/staking/move_stake.rs | 5 +++- pallets/subtensor/src/tests/move_stake.rs | 31 +++++++++++++++------ 2 files changed, 26 insertions(+), 10 deletions(-) diff --git a/pallets/subtensor/src/staking/move_stake.rs b/pallets/subtensor/src/staking/move_stake.rs index ee9cb49a7..701d8911d 100644 --- a/pallets/subtensor/src/staking/move_stake.rs +++ b/pallets/subtensor/src/staking/move_stake.rs @@ -181,7 +181,9 @@ impl<T: Config> Pallet<T> { ); // 6. Unstake from the origin coldkey; this returns an amount of TAO. - let origin_tao = Self::unstake_from_subnet(&hotkey, &coldkey, origin_netuid, alpha_amount); + let fee = DefaultMinStake::<T>::get().saturating_div(2); + let origin_tao = + Self::unstake_from_subnet(&hotkey, &coldkey, origin_netuid, alpha_amount, fee); // 7. Ensure the returned TAO meets a minimum stake requirement (if required). ensure!( @@ -196,6 +198,7 @@ impl<T: Config> Pallet<T> { &destination_coldkey, destination_netuid, origin_tao, + fee, ); // 9. Emit an event for logging/monitoring. diff --git a/pallets/subtensor/src/tests/move_stake.rs b/pallets/subtensor/src/tests/move_stake.rs index 3c1f2b448..a8896eef4 100644 --- a/pallets/subtensor/src/tests/move_stake.rs +++ b/pallets/subtensor/src/tests/move_stake.rs @@ -813,6 +813,7 @@ fn test_do_transfer_success() { 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 fee = DefaultMinStake::<Test>::get(); // 2. Define the origin coldkey, destination coldkey, and hotkey to be used. let origin_coldkey = U256::from(1); @@ -823,7 +824,7 @@ fn test_do_transfer_success() { // 3. Set up initial stake: (origin_coldkey, hotkey) on netuid. SubtensorModule::create_account_if_non_existent(&origin_coldkey, &hotkey); SubtensorModule::create_account_if_non_existent(&destination_coldkey, &hotkey); - SubtensorModule::stake_into_subnet(&hotkey, &origin_coldkey, netuid, stake_amount); + SubtensorModule::stake_into_subnet(&hotkey, &origin_coldkey, netuid, stake_amount, 0); let alpha = SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( &hotkey, &origin_coldkey, @@ -855,7 +856,7 @@ fn test_do_transfer_success() { &destination_coldkey, netuid ), - stake_amount, + stake_amount - fee, epsilon = stake_amount / 1000 ); }); @@ -922,7 +923,7 @@ fn test_do_transfer_insufficient_stake() { let stake_amount = DefaultMinStake::<Test>::get() * 10; SubtensorModule::create_account_if_non_existent(&origin_coldkey, &hotkey); - SubtensorModule::stake_into_subnet(&hotkey, &origin_coldkey, netuid, stake_amount); + SubtensorModule::stake_into_subnet(&hotkey, &origin_coldkey, netuid, stake_amount, 0); let alpha = stake_amount * 2; assert_noop!( @@ -950,11 +951,12 @@ fn test_do_transfer_wrong_origin() { let wrong_coldkey = U256::from(9999); let destination_coldkey = U256::from(2); let hotkey = U256::from(3); - let stake_amount = 100_000; + let stake_amount = DefaultMinStake::<Test>::get() * 10; + let fee = DefaultMinStake::<Test>::get(); SubtensorModule::create_account_if_non_existent(&origin_coldkey, &hotkey); - SubtensorModule::add_balance_to_coldkey_account(&origin_coldkey, 1_000_000_000); - SubtensorModule::stake_into_subnet(&hotkey, &origin_coldkey, netuid, stake_amount); + SubtensorModule::add_balance_to_coldkey_account(&origin_coldkey, stake_amount + fee); + SubtensorModule::stake_into_subnet(&hotkey, &origin_coldkey, netuid, stake_amount, fee); assert_noop!( SubtensorModule::do_transfer_stake( @@ -983,7 +985,7 @@ fn test_do_transfer_minimum_stake_check() { let stake_amount = DefaultMinStake::<Test>::get(); SubtensorModule::create_account_if_non_existent(&origin_coldkey, &hotkey); - SubtensorModule::stake_into_subnet(&hotkey, &origin_coldkey, netuid, stake_amount); + SubtensorModule::stake_into_subnet(&hotkey, &origin_coldkey, netuid, stake_amount, 0); assert_err!( SubtensorModule::do_transfer_stake( @@ -1013,6 +1015,7 @@ fn test_do_transfer_different_subnets() { let destination_coldkey = U256::from(2); let hotkey = U256::from(3); let stake_amount = DefaultMinStake::<Test>::get() * 10; + let fee = DefaultMinStake::<Test>::get(); // 3. Create accounts if needed. SubtensorModule::create_account_if_non_existent(&origin_coldkey, &hotkey); @@ -1022,7 +1025,13 @@ fn test_do_transfer_different_subnets() { SubtensorModule::add_balance_to_coldkey_account(&origin_coldkey, 1_000_000_000); // 5. Stake into the origin subnet. - SubtensorModule::stake_into_subnet(&hotkey, &origin_coldkey, origin_netuid, stake_amount); + SubtensorModule::stake_into_subnet( + &hotkey, + &origin_coldkey, + origin_netuid, + stake_amount, + 0, + ); // 6. Transfer entire stake from origin_netuid -> destination_netuid. let alpha = SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( @@ -1055,6 +1064,10 @@ fn test_do_transfer_different_subnets() { &destination_coldkey, destination_netuid, ); - assert_abs_diff_eq!(dest_stake, stake_amount, epsilon = stake_amount / 1000); + assert_abs_diff_eq!( + dest_stake, + stake_amount - fee, + epsilon = stake_amount / 1000 + ); }); } From 7d43c7e6bbc1d900a8c168967a208991da0dfc56 Mon Sep 17 00:00:00 2001 From: JohnReedV <87283488+JohnReedV@users.noreply.github.com> Date: Wed, 22 Jan 2025 10:58:30 -0800 Subject: [PATCH 036/145] add swap_stake & tests --- pallets/subtensor/src/macros/dispatches.rs | 44 ++- pallets/subtensor/src/macros/events.rs | 6 + pallets/subtensor/src/staking/move_stake.rs | 97 ++++++ pallets/subtensor/src/tests/move_stake.rs | 360 ++++++++++++++++++++ 4 files changed, 504 insertions(+), 3 deletions(-) diff --git a/pallets/subtensor/src/macros/dispatches.rs b/pallets/subtensor/src/macros/dispatches.rs index 34712ccb5..7a3e3b240 100644 --- a/pallets/subtensor/src/macros/dispatches.rs +++ b/pallets/subtensor/src/macros/dispatches.rs @@ -1608,9 +1608,6 @@ mod dispatches { /// * `destination_netuid` - The network/subnet ID to move stake to (for cross-subnet transfer). /// * `alpha_amount` - The amount of stake to transfer. /// - /// # Weight - /// Uses a fixed weight of 3_000_000 (plus any DB write overhead). - /// /// # Errors /// Returns an error if: /// * The origin is not signed by the correct coldkey. @@ -1640,5 +1637,46 @@ mod dispatches { alpha_amount, ) } + + /// Swaps a specified amount of stake from one subnet to another, while keeping the same coldkey and hotkey. + /// + /// # Arguments + /// * `origin` - The origin of the transaction, which must be signed by the coldkey that owns the `hotkey`. + /// * `hotkey` - The hotkey whose stake is being swapped. + /// * `origin_netuid` - The network/subnet ID from which stake is removed. + /// * `destination_netuid` - The network/subnet ID to which stake is added. + /// * `alpha_amount` - The amount of stake to swap. + /// + /// # Errors + /// Returns an error if: + /// * The transaction is not signed by the correct coldkey (i.e., `coldkey_owns_hotkey` fails). + /// * Either `origin_netuid` or `destination_netuid` does not exist. + /// * The hotkey does not exist. + /// * There is insufficient stake on `(coldkey, hotkey, origin_netuid)`. + /// * The swap amount is below the minimum stake requirement. + /// + /// # Events + /// May emit a `StakeSwapped` event on success. + #[pallet::call_index(87)] + #[pallet::weight(( + Weight::from_parts(3_000_000, 0).saturating_add(T::DbWeight::get().writes(1)), + DispatchClass::Operational, + Pays::No + ))] + pub fn swap_stake( + origin: T::RuntimeOrigin, + hotkey: T::AccountId, + origin_netuid: u16, + destination_netuid: u16, + alpha_amount: u64, + ) -> DispatchResult { + Self::do_swap_stake( + origin, + hotkey, + origin_netuid, + destination_netuid, + alpha_amount, + ) + } } } diff --git a/pallets/subtensor/src/macros/events.rs b/pallets/subtensor/src/macros/events.rs index 782f6b792..9083e57f6 100644 --- a/pallets/subtensor/src/macros/events.rs +++ b/pallets/subtensor/src/macros/events.rs @@ -253,5 +253,11 @@ mod events { /// Parameters: /// (origin_coldkey, destination_coldkey, hotkey, origin_netuid, destination_netuid, amount) StakeTransferred(T::AccountId, T::AccountId, T::AccountId, u16, u16, u64), + + /// Stake has been swapped from one subnet to another for the same coldkey-hotkey pair. + /// + /// Parameters: + /// (coldkey, hotkey, origin_netuid, destination_netuid, amount) + StakeSwapped(T::AccountId, T::AccountId, u16, u16, u64), } } diff --git a/pallets/subtensor/src/staking/move_stake.rs b/pallets/subtensor/src/staking/move_stake.rs index 49d823b86..60667e453 100644 --- a/pallets/subtensor/src/staking/move_stake.rs +++ b/pallets/subtensor/src/staking/move_stake.rs @@ -217,4 +217,101 @@ impl<T: Config> Pallet<T> { // 10. Return success. Ok(()) } + + /// Swaps a specified amount of stake for the same `(coldkey, hotkey)` pair from one subnet + /// (`origin_netuid`) to another (`destination_netuid`). + /// + /// # Arguments + /// * `origin` - The origin of the transaction, which must be signed by the coldkey that owns the hotkey. + /// * `hotkey` - The hotkey whose stake is being swapped. + /// * `origin_netuid` - The subnet ID from which stake is removed. + /// * `destination_netuid` - The subnet ID to which stake is added. + /// * `alpha_amount` - The amount of stake to swap. + /// + /// # Returns + /// * `DispatchResult` - Indicates success or failure. + /// + /// # Errors + /// This function returns an error if: + /// * The origin is not signed by the correct coldkey (i.e., not associated with `hotkey`). + /// * Either the `origin_netuid` or the `destination_netuid` does not exist. + /// * The specified `hotkey` does not exist. + /// * The `(coldkey, hotkey, origin_netuid)` does not have enough stake (`alpha_amount`). + /// * The unstaked amount is below `DefaultMinStake`. + /// + /// # Events + /// Emits a `StakeSwapped` event upon successful completion. + pub fn do_swap_stake( + origin: T::RuntimeOrigin, + hotkey: T::AccountId, + origin_netuid: u16, + destination_netuid: u16, + alpha_amount: u64, + ) -> dispatch::DispatchResult { + // 1. Ensure the extrinsic is signed by the coldkey. + let coldkey = ensure_signed(origin)?; + + // 2. Check that both subnets exist. + ensure!( + Self::if_subnet_exist(origin_netuid), + Error::<T>::SubnetNotExists + ); + ensure!( + Self::if_subnet_exist(destination_netuid), + Error::<T>::SubnetNotExists + ); + + // 3. Check that the hotkey exists. + ensure!( + Self::hotkey_account_exists(&hotkey), + Error::<T>::HotKeyAccountNotExists + ); + + // 4. Ensure this coldkey actually owns the hotkey. + ensure!( + Self::coldkey_owns_hotkey(&coldkey, &hotkey), + Error::<T>::NonAssociatedColdKey + ); + + // 5. Ensure there is enough stake in the origin subnet. + let origin_alpha = + Self::get_stake_for_hotkey_and_coldkey_on_subnet(&hotkey, &coldkey, origin_netuid); + ensure!( + alpha_amount <= origin_alpha, + Error::<T>::NotEnoughStakeToWithdraw + ); + + // 6. Unstake from the origin subnet, returning TAO (or a 1:1 equivalent). + let tao_unstaked = + Self::unstake_from_subnet(&hotkey, &coldkey, origin_netuid, alpha_amount); + + // 7. Check that the unstaked amount is above the minimum stake threshold. + ensure!( + tao_unstaked >= DefaultMinStake::<T>::get(), + Error::<T>::AmountTooLow + ); + + // 8. Stake the unstaked amount into the destination subnet, using the same coldkey/hotkey. + Self::stake_into_subnet(&hotkey, &coldkey, destination_netuid, tao_unstaked); + + // 9. Emit an event for logging. + log::info!( + "StakeSwapped(coldkey: {:?}, hotkey: {:?}, origin_netuid: {:?}, destination_netuid: {:?}, amount: {:?})", + coldkey, + hotkey, + origin_netuid, + destination_netuid, + tao_unstaked + ); + Self::deposit_event(Event::StakeSwapped( + coldkey, + hotkey, + origin_netuid, + destination_netuid, + tao_unstaked, + )); + + // 10. Return success. + Ok(()) + } } diff --git a/pallets/subtensor/src/tests/move_stake.rs b/pallets/subtensor/src/tests/move_stake.rs index 59afc9b62..b15f49d92 100644 --- a/pallets/subtensor/src/tests/move_stake.rs +++ b/pallets/subtensor/src/tests/move_stake.rs @@ -1026,3 +1026,363 @@ fn test_do_transfer_different_subnets() { assert_abs_diff_eq!(dest_stake, stake_amount, epsilon = stake_amount / 1000); }); } + +#[test] +fn test_do_swap_success() { + 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 hotkey = U256::from(2); + let stake_amount = DefaultMinStake::<Test>::get() * 10; + + SubtensorModule::create_account_if_non_existent(&coldkey, &hotkey); + SubtensorModule::stake_into_subnet(&hotkey, &coldkey, origin_netuid, stake_amount); + let alpha_before = SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( + &hotkey, + &coldkey, + origin_netuid, + ); + + assert_ok!(SubtensorModule::do_swap_stake( + RuntimeOrigin::signed(coldkey), + hotkey, + origin_netuid, + destination_netuid, + alpha_before, + )); + + assert_eq!( + SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( + &hotkey, + &coldkey, + origin_netuid + ), + 0 + ); + + let alpha_after = SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( + &hotkey, + &coldkey, + destination_netuid, + ); + assert_abs_diff_eq!(alpha_after, stake_amount, epsilon = stake_amount / 1000); + }); +} + +#[test] +fn test_do_swap_nonexistent_subnet() { + new_test_ext(1).execute_with(|| { + let coldkey = U256::from(1); + let hotkey = U256::from(2); + let nonexistent_netuid: u16 = 9999; + let stake_amount = 1_000_000; + + SubtensorModule::create_account_if_non_existent(&coldkey, &hotkey); + + assert_noop!( + SubtensorModule::do_swap_stake( + RuntimeOrigin::signed(coldkey), + hotkey, + nonexistent_netuid, + nonexistent_netuid, + stake_amount + ), + Error::<Test>::SubnetNotExists + ); + }); +} + +#[test] +fn test_do_swap_nonexistent_hotkey() { + new_test_ext(1).execute_with(|| { + let subnet_owner_coldkey = U256::from(1001); + let subnet_owner_hotkey = U256::from(1002); + let netuid1 = add_dynamic_network(&subnet_owner_hotkey, &subnet_owner_coldkey); + let netuid2 = add_dynamic_network(&subnet_owner_hotkey, &subnet_owner_coldkey); + + let coldkey = U256::from(1); + let nonexistent_hotkey = U256::from(999); + let stake_amount = 10_000; + + assert_noop!( + SubtensorModule::do_swap_stake( + RuntimeOrigin::signed(coldkey), + nonexistent_hotkey, + netuid1, + netuid2, + stake_amount + ), + Error::<Test>::HotKeyAccountNotExists + ); + }); +} + +#[test] +fn test_do_swap_insufficient_stake() { + new_test_ext(1).execute_with(|| { + let subnet_owner_coldkey = U256::from(1001); + let subnet_owner_hotkey = U256::from(1002); + let netuid1 = add_dynamic_network(&subnet_owner_hotkey, &subnet_owner_coldkey); + let netuid2 = add_dynamic_network(&subnet_owner_hotkey, &subnet_owner_coldkey); + + let coldkey = U256::from(1); + let hotkey = U256::from(2); + let stake_amount = DefaultMinStake::<Test>::get() * 5; + let attempted_swap = stake_amount * 2; + + SubtensorModule::create_account_if_non_existent(&coldkey, &hotkey); + SubtensorModule::stake_into_subnet(&hotkey, &coldkey, netuid1, stake_amount); + + assert_noop!( + SubtensorModule::do_swap_stake( + RuntimeOrigin::signed(coldkey), + hotkey, + netuid1, + netuid2, + attempted_swap + ), + Error::<Test>::NotEnoughStakeToWithdraw + ); + }); +} + +#[test] +fn test_do_swap_wrong_origin() { + new_test_ext(1).execute_with(|| { + let subnet_owner_coldkey = U256::from(1010); + let subnet_owner_hotkey = U256::from(1011); + let netuid1 = add_dynamic_network(&subnet_owner_hotkey, &subnet_owner_coldkey); + let netuid2 = add_dynamic_network(&subnet_owner_hotkey, &subnet_owner_coldkey); + + let real_coldkey = U256::from(1); + let wrong_coldkey = U256::from(9999); + let hotkey = U256::from(3); + let stake_amount = 100_000; + + SubtensorModule::create_account_if_non_existent(&real_coldkey, &hotkey); + SubtensorModule::stake_into_subnet(&hotkey, &real_coldkey, netuid1, stake_amount); + + assert_noop!( + SubtensorModule::do_swap_stake( + RuntimeOrigin::signed(wrong_coldkey), + hotkey, + netuid1, + netuid2, + stake_amount + ), + Error::<Test>::NonAssociatedColdKey + ); + }); +} + +#[test] +fn test_do_swap_minimum_stake_check() { + 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(3); + let total_stake = DefaultMinStake::<Test>::get(); + let swap_amount = 1; + + SubtensorModule::create_account_if_non_existent(&coldkey, &hotkey); + SubtensorModule::stake_into_subnet(&hotkey, &coldkey, netuid, total_stake); + + assert_err!( + SubtensorModule::do_swap_stake( + RuntimeOrigin::signed(coldkey), + hotkey, + netuid, + netuid, + swap_amount + ), + Error::<Test>::AmountTooLow + ); + }); +} + +#[test] +fn test_do_swap_same_subnet() { + new_test_ext(1).execute_with(|| { + let subnet_owner_coldkey = U256::from(1100); + let subnet_owner_hotkey = U256::from(1101); + let netuid = add_dynamic_network(&subnet_owner_hotkey, &subnet_owner_coldkey); + + let coldkey = U256::from(1); + let hotkey = U256::from(2); + let stake_amount = DefaultMinStake::<Test>::get() * 10; + + SubtensorModule::create_account_if_non_existent(&coldkey, &hotkey); + SubtensorModule::stake_into_subnet(&hotkey, &coldkey, netuid, stake_amount); + + let alpha_before = + SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet(&hotkey, &coldkey, netuid); + + assert_ok!(SubtensorModule::do_swap_stake( + RuntimeOrigin::signed(coldkey), + hotkey, + netuid, + netuid, + alpha_before + )); + + let alpha_after = + SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet(&hotkey, &coldkey, netuid); + assert_abs_diff_eq!(alpha_after, alpha_before, epsilon = 5); + }); +} + +#[test] +fn test_do_swap_partial_stake() { + new_test_ext(1).execute_with(|| { + let subnet_owner_coldkey = U256::from(1100); + let subnet_owner_hotkey = U256::from(1101); + 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 hotkey = U256::from(2); + let total_stake = DefaultMinStake::<Test>::get() * 10; + + SubtensorModule::create_account_if_non_existent(&coldkey, &hotkey); + SubtensorModule::stake_into_subnet(&hotkey, &coldkey, origin_netuid, total_stake); + + let swap_amount = total_stake / 2; + assert_ok!(SubtensorModule::do_swap_stake( + RuntimeOrigin::signed(coldkey), + hotkey, + origin_netuid, + destination_netuid, + swap_amount, + )); + + assert_abs_diff_eq!( + SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( + &hotkey, + &coldkey, + origin_netuid + ), + total_stake - swap_amount, + epsilon = total_stake / 1000 + ); + assert_abs_diff_eq!( + SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( + &hotkey, + &coldkey, + destination_netuid + ), + swap_amount, + epsilon = total_stake / 1000 + ); + }); +} + +#[test] +fn test_do_swap_storage_updates() { + new_test_ext(1).execute_with(|| { + let subnet_owner_coldkey = U256::from(1300); + let subnet_owner_hotkey = U256::from(1301); + 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 hotkey = U256::from(2); + let stake_amount = DefaultMinStake::<Test>::get() * 10; + + SubtensorModule::create_account_if_non_existent(&coldkey, &hotkey); + SubtensorModule::stake_into_subnet(&hotkey, &coldkey, origin_netuid, stake_amount); + + let alpha = SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( + &hotkey, + &coldkey, + origin_netuid, + ); + assert_ok!(SubtensorModule::do_swap_stake( + RuntimeOrigin::signed(coldkey), + hotkey, + origin_netuid, + destination_netuid, + alpha + )); + + assert_eq!( + SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( + &hotkey, + &coldkey, + origin_netuid + ), + 0 + ); + + assert_abs_diff_eq!( + SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( + &hotkey, + &coldkey, + destination_netuid + ), + alpha, + epsilon = 5 + ); + }); +} + +#[test] +fn test_do_swap_multiple_times() { + new_test_ext(1).execute_with(|| { + let subnet_owner_coldkey = U256::from(1500); + let subnet_owner_hotkey = U256::from(1501); + let netuid1 = add_dynamic_network(&subnet_owner_hotkey, &subnet_owner_coldkey); + let netuid2 = add_dynamic_network(&subnet_owner_hotkey, &subnet_owner_coldkey); + + let coldkey = U256::from(1); + let hotkey = U256::from(2); + let initial_stake = DefaultMinStake::<Test>::get() * 10; + + SubtensorModule::create_account_if_non_existent(&coldkey, &hotkey); + SubtensorModule::stake_into_subnet(&hotkey, &coldkey, netuid1, initial_stake); + + for _ in 0..3 { + let alpha1 = SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( + &hotkey, &coldkey, netuid1, + ); + if alpha1 > 0 { + assert_ok!(SubtensorModule::do_swap_stake( + RuntimeOrigin::signed(coldkey), + hotkey, + netuid1, + netuid2, + alpha1 + )); + } + let alpha2 = SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( + &hotkey, &coldkey, netuid2, + ); + if alpha2 > 0 { + assert_ok!(SubtensorModule::do_swap_stake( + RuntimeOrigin::signed(coldkey), + hotkey, + netuid2, + netuid1, + alpha2 + )); + } + } + + let final_stake_netuid1 = + SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet(&hotkey, &coldkey, netuid1); + let final_stake_netuid2 = + SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet(&hotkey, &coldkey, netuid2); + assert_abs_diff_eq!( + final_stake_netuid1, + initial_stake, + epsilon = initial_stake / 1000 + ); + assert_eq!(final_stake_netuid2, 0); + }); +} From 845cba99e605bde195f3fb8d4d705131d466bbb0 Mon Sep 17 00:00:00 2001 From: Greg Zaitsev <gregz@opentensor.dev> Date: Wed, 22 Jan 2025 13:58:46 -0500 Subject: [PATCH 037/145] Make commitment pallet rate limit configurable via sudo --- pallets/commitments/src/lib.rs | 27 +++++++++++++++++++++++++-- pallets/commitments/src/tests.rs | 2 +- pallets/commitments/src/weights.rs | 12 ++++++++++++ runtime/src/lib.rs | 2 +- 4 files changed, 39 insertions(+), 4 deletions(-) diff --git a/pallets/commitments/src/lib.rs b/pallets/commitments/src/lib.rs index d5d132034..7e862a593 100644 --- a/pallets/commitments/src/lib.rs +++ b/pallets/commitments/src/lib.rs @@ -58,7 +58,7 @@ pub mod pallet { /// The rate limit for commitments #[pallet::constant] - type RateLimit: Get<BlockNumberFor<Self>>; + type DefaultRateLimit: Get<BlockNumberFor<Self>>; } #[pallet::event] @@ -83,6 +83,16 @@ pub mod pallet { CommitmentSetRateLimitExceeded, } + #[pallet::type_value] + /// Default value for commitment rate limit. + pub fn DefaultRateLimit<T: Config>() -> BlockNumberFor<T> { + T::DefaultRateLimit::get() + } + + /// The rate limit for commitments + #[pallet::storage] + pub type RateLimit<T> = StorageValue<_, BlockNumberFor<T>, ValueQuery, DefaultRateLimit<T>>; + /// Identity data by account #[pallet::storage] #[pallet::getter(fn commitment_of)] @@ -137,7 +147,7 @@ pub mod pallet { let cur_block = <frame_system::Pallet<T>>::block_number(); if let Some(last_commit) = <LastCommitment<T>>::get(netuid, &who) { ensure!( - cur_block >= last_commit.saturating_add(T::RateLimit::get()), + cur_block >= last_commit.saturating_add(RateLimit::<T>::get()), Error::<T>::CommitmentSetRateLimitExceeded ); } @@ -173,6 +183,19 @@ pub mod pallet { Ok(()) } + + /// Sudo-set the commitment rate limit + #[pallet::call_index(1)] + #[pallet::weight(( + T::WeightInfo::set_rate_limit(), + DispatchClass::Operational, + Pays::No + ))] + pub fn set_rate_limit(origin: OriginFor<T>, rate_limit_blocks: u32) -> DispatchResult { + ensure_root(origin)?; + RateLimit::<T>::set(rate_limit_blocks.into()); + Ok(()) + } } } diff --git a/pallets/commitments/src/tests.rs b/pallets/commitments/src/tests.rs index 036cb5d83..15675d8ad 100644 --- a/pallets/commitments/src/tests.rs +++ b/pallets/commitments/src/tests.rs @@ -86,7 +86,7 @@ impl pallet_commitments::Config for Test { type CanCommit = (); type FieldDeposit = frame_support::traits::ConstU64<0>; type InitialDeposit = frame_support::traits::ConstU64<0>; - type RateLimit = frame_support::traits::ConstU64<0>; + type DefaultRateLimit = frame_support::traits::ConstU64<0>; } // // Build genesis storage according to the mock runtime. diff --git a/pallets/commitments/src/weights.rs b/pallets/commitments/src/weights.rs index 5e58b6873..b91017e05 100644 --- a/pallets/commitments/src/weights.rs +++ b/pallets/commitments/src/weights.rs @@ -30,6 +30,7 @@ use core::marker::PhantomData; /// Weight functions needed for `pallet_commitments`. pub trait WeightInfo { fn set_commitment() -> Weight; + fn set_rate_limit() -> Weight; } /// Weights for `pallet_commitments` using the Substrate node and recommended hardware. @@ -48,6 +49,11 @@ impl<T: frame_system::Config> WeightInfo for SubstrateWeight<T> { .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } + /// Sudo setting rate limit for commitments + fn set_rate_limit() -> Weight { + Weight::from_parts(10_000_000, 2000) + .saturating_add(RocksDbWeight::get().reads(1_u64)) + } } // For backwards compatibility and tests. @@ -65,4 +71,10 @@ impl WeightInfo for () { .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } + + /// Sudo setting rate limit for commitments + fn set_rate_limit() -> Weight { + Weight::from_parts(10_000_000, 2000) + .saturating_add(RocksDbWeight::get().reads(1_u64)) + } } \ No newline at end of file diff --git a/runtime/src/lib.rs b/runtime/src/lib.rs index 5c3688a51..36a879e48 100644 --- a/runtime/src/lib.rs +++ b/runtime/src/lib.rs @@ -974,7 +974,7 @@ impl pallet_commitments::Config for Runtime { type MaxFields = MaxCommitFields; type InitialDeposit = CommitmentInitialDeposit; type FieldDeposit = CommitmentFieldDeposit; - type RateLimit = CommitmentRateLimit; + type DefaultRateLimit = CommitmentRateLimit; } #[cfg(not(feature = "fast-blocks"))] From feddc59ca17e16699239a884827c874e4896291f Mon Sep 17 00:00:00 2001 From: camfairchild <cameron@opentensor.dev> Date: Wed, 22 Jan 2025 15:11:44 -0500 Subject: [PATCH 038/145] remove root divs before alpha payout --- pallets/subtensor/src/coinbase/run_coinbase.rs | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/pallets/subtensor/src/coinbase/run_coinbase.rs b/pallets/subtensor/src/coinbase/run_coinbase.rs index e86c07c24..ddb045556 100644 --- a/pallets/subtensor/src/coinbase/run_coinbase.rs +++ b/pallets/subtensor/src/coinbase/run_coinbase.rs @@ -472,6 +472,15 @@ impl<T: Config> Pallet<T> { rem_divs_j ); + let possible_root_divs = *root_alpha_divs.get(hotkey_j).unwrap_or(&0); + let alpha_divs: I96F32 = + rem_divs_j.saturating_sub(I96F32::from_num(possible_root_divs)); + log::debug!( + "Removed root divs for hotkey {:?}: {:?}", + hotkey_j, + possible_root_divs + ); + // Distribute validator take. Self::increase_stake_for_hotkey_and_coldkey_on_subnet( hotkey_j, @@ -490,18 +499,20 @@ impl<T: Config> Pallet<T> { Self::increase_stake_for_hotkey_on_subnet( hotkey_j, netuid, - rem_divs_j.to_num::<u64>(), + alpha_divs.to_num::<u64>(), ); log::debug!( "Distributed alpha dividends for hotkey {:?} on netuid {:?}: {:?}", hotkey_j, netuid, - rem_divs_j.to_num::<u64>() + alpha_divs.to_num::<u64>() ); // Record dividends for this hotkey on this subnet. AlphaDividendsPerSubnet::<T>::mutate(netuid, hotkey_j.clone(), |divs| { - *divs = divs.saturating_add(*divs_j); + *divs = divs + .saturating_add(alpha_divs.to_num::<u64>()) + .saturating_add(validator_take.to_num::<u64>()); }); log::debug!( "Recorded dividends for hotkey {:?} on netuid {:?}: {:?}", From 9dc0ecec678e7aa7b80917bade2b0aa1768010bd Mon Sep 17 00:00:00 2001 From: camfairchild <cameron@opentensor.dev> Date: Wed, 22 Jan 2025 14:46:27 -0500 Subject: [PATCH 039/145] charge fee for swap and add for tests --- pallets/subtensor/src/staking/move_stake.rs | 5 +++-- pallets/subtensor/src/tests/move_stake.rs | 16 ++++++++-------- 2 files changed, 11 insertions(+), 10 deletions(-) diff --git a/pallets/subtensor/src/staking/move_stake.rs b/pallets/subtensor/src/staking/move_stake.rs index 38a783b25..fff688084 100644 --- a/pallets/subtensor/src/staking/move_stake.rs +++ b/pallets/subtensor/src/staking/move_stake.rs @@ -288,8 +288,9 @@ impl<T: Config> Pallet<T> { ); // 6. Unstake from the origin subnet, returning TAO (or a 1:1 equivalent). + let fee = DefaultMinStake::<T>::get().saturating_div(2); let tao_unstaked = - Self::unstake_from_subnet(&hotkey, &coldkey, origin_netuid, alpha_amount); + Self::unstake_from_subnet(&hotkey, &coldkey, origin_netuid, alpha_amount, fee); // 7. Check that the unstaked amount is above the minimum stake threshold. ensure!( @@ -298,7 +299,7 @@ impl<T: Config> Pallet<T> { ); // 8. Stake the unstaked amount into the destination subnet, using the same coldkey/hotkey. - Self::stake_into_subnet(&hotkey, &coldkey, destination_netuid, tao_unstaked); + Self::stake_into_subnet(&hotkey, &coldkey, destination_netuid, tao_unstaked, fee); // 9. Emit an event for logging. log::info!( diff --git a/pallets/subtensor/src/tests/move_stake.rs b/pallets/subtensor/src/tests/move_stake.rs index 150fa1201..04f79f3f9 100644 --- a/pallets/subtensor/src/tests/move_stake.rs +++ b/pallets/subtensor/src/tests/move_stake.rs @@ -1085,7 +1085,7 @@ fn test_do_swap_success() { let stake_amount = DefaultMinStake::<Test>::get() * 10; SubtensorModule::create_account_if_non_existent(&coldkey, &hotkey); - SubtensorModule::stake_into_subnet(&hotkey, &coldkey, origin_netuid, stake_amount); + SubtensorModule::stake_into_subnet(&hotkey, &coldkey, origin_netuid, stake_amount, 0); let alpha_before = SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( &hotkey, &coldkey, @@ -1180,7 +1180,7 @@ fn test_do_swap_insufficient_stake() { let attempted_swap = stake_amount * 2; SubtensorModule::create_account_if_non_existent(&coldkey, &hotkey); - SubtensorModule::stake_into_subnet(&hotkey, &coldkey, netuid1, stake_amount); + SubtensorModule::stake_into_subnet(&hotkey, &coldkey, netuid1, stake_amount, 0); assert_noop!( SubtensorModule::do_swap_stake( @@ -1209,7 +1209,7 @@ fn test_do_swap_wrong_origin() { let stake_amount = 100_000; SubtensorModule::create_account_if_non_existent(&real_coldkey, &hotkey); - SubtensorModule::stake_into_subnet(&hotkey, &real_coldkey, netuid1, stake_amount); + SubtensorModule::stake_into_subnet(&hotkey, &real_coldkey, netuid1, stake_amount, 0); assert_noop!( SubtensorModule::do_swap_stake( @@ -1237,7 +1237,7 @@ fn test_do_swap_minimum_stake_check() { let swap_amount = 1; SubtensorModule::create_account_if_non_existent(&coldkey, &hotkey); - SubtensorModule::stake_into_subnet(&hotkey, &coldkey, netuid, total_stake); + SubtensorModule::stake_into_subnet(&hotkey, &coldkey, netuid, total_stake, 0); assert_err!( SubtensorModule::do_swap_stake( @@ -1264,7 +1264,7 @@ fn test_do_swap_same_subnet() { let stake_amount = DefaultMinStake::<Test>::get() * 10; SubtensorModule::create_account_if_non_existent(&coldkey, &hotkey); - SubtensorModule::stake_into_subnet(&hotkey, &coldkey, netuid, stake_amount); + SubtensorModule::stake_into_subnet(&hotkey, &coldkey, netuid, stake_amount, 0); let alpha_before = SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet(&hotkey, &coldkey, netuid); @@ -1296,7 +1296,7 @@ fn test_do_swap_partial_stake() { let total_stake = DefaultMinStake::<Test>::get() * 10; SubtensorModule::create_account_if_non_existent(&coldkey, &hotkey); - SubtensorModule::stake_into_subnet(&hotkey, &coldkey, origin_netuid, total_stake); + SubtensorModule::stake_into_subnet(&hotkey, &coldkey, origin_netuid, total_stake, 0); let swap_amount = total_stake / 2; assert_ok!(SubtensorModule::do_swap_stake( @@ -1341,7 +1341,7 @@ fn test_do_swap_storage_updates() { let stake_amount = DefaultMinStake::<Test>::get() * 10; SubtensorModule::create_account_if_non_existent(&coldkey, &hotkey); - SubtensorModule::stake_into_subnet(&hotkey, &coldkey, origin_netuid, stake_amount); + SubtensorModule::stake_into_subnet(&hotkey, &coldkey, origin_netuid, stake_amount, 0); let alpha = SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( &hotkey, @@ -1390,7 +1390,7 @@ fn test_do_swap_multiple_times() { let initial_stake = DefaultMinStake::<Test>::get() * 10; SubtensorModule::create_account_if_non_existent(&coldkey, &hotkey); - SubtensorModule::stake_into_subnet(&hotkey, &coldkey, netuid1, initial_stake); + SubtensorModule::stake_into_subnet(&hotkey, &coldkey, netuid1, initial_stake, 0); for _ in 0..3 { let alpha1 = SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( From 6afea18909604a1b8956de7a1b54f2b17e266520 Mon Sep 17 00:00:00 2001 From: camfairchild <cameron@opentensor.dev> Date: Wed, 22 Jan 2025 15:55:35 -0500 Subject: [PATCH 040/145] calc root prop using parent key --- .../subtensor/src/coinbase/run_coinbase.rs | 46 +++++++++---------- 1 file changed, 23 insertions(+), 23 deletions(-) diff --git a/pallets/subtensor/src/coinbase/run_coinbase.rs b/pallets/subtensor/src/coinbase/run_coinbase.rs index ddb045556..7e73ea8eb 100644 --- a/pallets/subtensor/src/coinbase/run_coinbase.rs +++ b/pallets/subtensor/src/coinbase/run_coinbase.rs @@ -356,29 +356,6 @@ impl<T: Config> Pallet<T> { // Calculate the validator take and root alpha divs using the alpha divs. for (hotkey, dividend_tuples) in dividends_to_distribute.iter() { - // Get the local alpha and root alpha. - let hotkey_tao: I96F32 = I96F32::from_num(Self::get_stake_for_hotkey_on_subnet( - hotkey, - Self::get_root_netuid(), - )); - let hotkey_tao_as_alpha: I96F32 = hotkey_tao.saturating_mul(Self::get_tao_weight()); - let hotkey_alpha = - I96F32::from_num(Self::get_stake_for_hotkey_on_subnet(hotkey, netuid)); - log::debug!("Hotkey tao for hotkey {:?} on root netuid: {:?}, hotkey tao as alpha: {:?}, hotkey alpha: {:?}", hotkey, hotkey_tao, hotkey_tao_as_alpha, hotkey_alpha); - - // Compute alpha and root proportions. - let alpha_prop: I96F32 = hotkey_alpha - .checked_div(hotkey_alpha.saturating_add(hotkey_tao_as_alpha)) - .unwrap_or(I96F32::from_num(0.0)); - let root_prop: I96F32 = hotkey_tao_as_alpha - .checked_div(hotkey_alpha.saturating_add(hotkey_tao_as_alpha)) - .unwrap_or(I96F32::from_num(0.0)); - log::debug!( - "Alpha proportion: {:?}, root proportion: {:?}", - alpha_prop, - root_prop - ); - // Calculate the dividends to hotkeys based on the local vs root proportion. for (hotkey_j, divs_j) in dividend_tuples.iter() { log::debug!( @@ -388,6 +365,29 @@ impl<T: Config> Pallet<T> { *divs_j ); + // Get the local alpha and root alpha. + let hotkey_tao: I96F32 = I96F32::from_num(Self::get_stake_for_hotkey_on_subnet( + hotkey_j, + Self::get_root_netuid(), + )); + let hotkey_tao_as_alpha: I96F32 = hotkey_tao.saturating_mul(Self::get_tao_weight()); + let hotkey_alpha = + I96F32::from_num(Self::get_stake_for_hotkey_on_subnet(hotkey_j, netuid)); + log::debug!("Hotkey tao for hotkey {:?} on root netuid: {:?}, hotkey tao as alpha: {:?}, hotkey alpha: {:?}", hotkey_j, hotkey_tao, hotkey_tao_as_alpha, hotkey_alpha); + + // Compute alpha and root proportions. + let alpha_prop: I96F32 = hotkey_alpha + .checked_div(hotkey_alpha.saturating_add(hotkey_tao_as_alpha)) + .unwrap_or(I96F32::from_num(0.0)); + let root_prop: I96F32 = hotkey_tao_as_alpha + .checked_div(hotkey_alpha.saturating_add(hotkey_tao_as_alpha)) + .unwrap_or(I96F32::from_num(0.0)); + log::debug!( + "Alpha proportion: {:?}, root proportion: {:?}", + alpha_prop, + root_prop + ); + // Remove the hotkey take straight off the top. let take_prop: I96F32 = I96F32::from_num(Self::get_hotkey_take(hotkey_j)) .checked_div(I96F32::from_num(u16::MAX)) From 51bb6a3c2f5aa7d6ffc4ea2c2fa937c039dd6c8e Mon Sep 17 00:00:00 2001 From: Greg Zaitsev <gregz@opentensor.dev> Date: Wed, 22 Jan 2025 15:59:18 -0500 Subject: [PATCH 041/145] Separate defaults for tx fee from min stake. Make min stake work like ED for stakes. --- pallets/subtensor/src/lib.rs | 20 ++++-- pallets/subtensor/src/staking/add_stake.rs | 10 ++- pallets/subtensor/src/staking/move_stake.rs | 15 +++-- pallets/subtensor/src/staking/remove_stake.rs | 6 +- pallets/subtensor/src/tests/epoch.rs | 4 +- pallets/subtensor/src/tests/move_stake.rs | 67 +++++++++++-------- pallets/subtensor/src/tests/senate.rs | 20 +++--- pallets/subtensor/src/tests/staking.rs | 26 +++---- pallets/subtensor/src/tests/swap_coldkey.rs | 14 ++-- pallets/subtensor/src/tests/swap_hotkey.rs | 2 +- pallets/subtensor/src/tests/weights.rs | 8 +-- 11 files changed, 108 insertions(+), 84 deletions(-) diff --git a/pallets/subtensor/src/lib.rs b/pallets/subtensor/src/lib.rs index 6ecb69c87..1a3f0e27b 100644 --- a/pallets/subtensor/src/lib.rs +++ b/pallets/subtensor/src/lib.rs @@ -702,11 +702,17 @@ pub mod pallet { #[pallet::type_value] /// Default minimum stake. /// 500k rao matches $0.25 at $500/TAO - /// Also used as staking fee pub fn DefaultMinStake<T: Config>() -> u64 { 500_000 } + #[pallet::type_value] + /// Default staking fee. + /// 500k rao matches $0.25 at $500/TAO + pub fn DefaultStakingFee<T: Config>() -> u64 { + 500_000 + } + #[pallet::type_value] /// Default unicode vector for tau symbol. pub fn DefaultUnicodeVecU8<T: Config>() -> Vec<u8> { @@ -1545,6 +1551,7 @@ pub enum CallType { pub enum CustomTransactionError { ColdkeyInSwapSchedule, StakeAmountTooLow, + BalanceTooLow, } impl From<CustomTransactionError> for u8 { @@ -1552,6 +1559,7 @@ impl From<CustomTransactionError> for u8 { match variant { CustomTransactionError::ColdkeyInSwapSchedule => 0, CustomTransactionError::StakeAmountTooLow => 1, + CustomTransactionError::BalanceTooLow => 2, } } } @@ -1704,7 +1712,7 @@ where Some(Call::add_stake { hotkey: _, netuid: _, - amount_staked, + amount, }) => { // Check that amount parameter is at least the min stake // also check the coldkey balance @@ -1713,12 +1721,14 @@ where >>::reducible_balance( who, Preservation::Expendable, Fortitude::Polite ); + let min_amount = + DefaultMinStake::<T>::get().saturating_add(DefaultStakingFee::<T>::get()); - if (*amount_staked < DefaultMinStake::<T>::get()) - || (coldkey_balance < DefaultMinStake::<T>::get()) - { + if *amount < min_amount { InvalidTransaction::Custom(CustomTransactionError::StakeAmountTooLow.into()) .into() + } else if coldkey_balance < min_amount { + InvalidTransaction::Custom(CustomTransactionError::BalanceTooLow.into()).into() } else { Ok(ValidTransaction { priority: Self::get_priority_vanilla(), diff --git a/pallets/subtensor/src/staking/add_stake.rs b/pallets/subtensor/src/staking/add_stake.rs index 66b337b3d..763899d6d 100644 --- a/pallets/subtensor/src/staking/add_stake.rs +++ b/pallets/subtensor/src/staking/add_stake.rs @@ -62,11 +62,9 @@ impl<T: Config> Pallet<T> { Error::<T>::HotKeyAccountNotExists ); - // Ensure stake_to_be_added is at least DefaultMinStake - ensure!( - stake_to_be_added >= DefaultMinStake::<T>::get(), - Error::<T>::AmountTooLow - ); + // Ensure stake_to_be_added is at least DefaultMinStake plus fee + let min_amount = DefaultMinStake::<T>::get().saturating_add(DefaultStakingFee::<T>::get()); + ensure!(stake_to_be_added >= min_amount, Error::<T>::AmountTooLow); // 5. Ensure the remove operation from the coldkey is a success. let tao_staked: u64 = @@ -74,7 +72,7 @@ impl<T: Config> Pallet<T> { // 6. Swap the stake into alpha on the subnet and increase counters. // Emit the staking event. - let fee = DefaultMinStake::<T>::get(); + let fee = DefaultStakingFee::<T>::get(); Self::stake_into_subnet(&hotkey, &coldkey, netuid, tao_staked, fee); // Ok and return. diff --git a/pallets/subtensor/src/staking/move_stake.rs b/pallets/subtensor/src/staking/move_stake.rs index 38a783b25..9c4ffabff 100644 --- a/pallets/subtensor/src/staking/move_stake.rs +++ b/pallets/subtensor/src/staking/move_stake.rs @@ -68,7 +68,7 @@ impl<T: Config> Pallet<T> { ); // --- 7. Unstake the amount of alpha from the origin subnet, converting it to TAO - let fee = DefaultMinStake::<T>::get().saturating_div(2); // fee is half of min stake because it is applied twice + let fee = DefaultStakingFee::<T>::get().saturating_div(2); // fee is half of min stake because it is applied twice let origin_tao = Self::unstake_from_subnet( &origin_hotkey.clone(), &coldkey.clone(), @@ -79,7 +79,7 @@ impl<T: Config> Pallet<T> { // Ensure origin_tao is at least DefaultMinStake ensure!( - origin_tao >= DefaultMinStake::<T>::get(), + origin_tao.saturating_sub(fee) >= DefaultMinStake::<T>::get(), Error::<T>::AmountTooLow ); @@ -181,13 +181,13 @@ impl<T: Config> Pallet<T> { ); // 6. Unstake from the origin coldkey; this returns an amount of TAO. - let fee = DefaultMinStake::<T>::get().saturating_div(2); + let fee = DefaultStakingFee::<T>::get().saturating_div(2); let origin_tao = Self::unstake_from_subnet(&hotkey, &coldkey, origin_netuid, alpha_amount, fee); // 7. Ensure the returned TAO meets a minimum stake requirement (if required). ensure!( - origin_tao >= DefaultMinStake::<T>::get(), + origin_tao.saturating_sub(fee) >= DefaultMinStake::<T>::get(), Error::<T>::AmountTooLow ); @@ -288,17 +288,18 @@ impl<T: Config> Pallet<T> { ); // 6. Unstake from the origin subnet, returning TAO (or a 1:1 equivalent). + let fee = DefaultStakingFee::<T>::get().saturating_div(2); let tao_unstaked = - Self::unstake_from_subnet(&hotkey, &coldkey, origin_netuid, alpha_amount); + Self::unstake_from_subnet(&hotkey, &coldkey, origin_netuid, alpha_amount, fee); // 7. Check that the unstaked amount is above the minimum stake threshold. ensure!( - tao_unstaked >= DefaultMinStake::<T>::get(), + tao_unstaked.saturating_sub(fee) >= DefaultMinStake::<T>::get(), Error::<T>::AmountTooLow ); // 8. Stake the unstaked amount into the destination subnet, using the same coldkey/hotkey. - Self::stake_into_subnet(&hotkey, &coldkey, destination_netuid, tao_unstaked); + Self::stake_into_subnet(&hotkey, &coldkey, destination_netuid, tao_unstaked, fee); // 9. Emit an event for logging. log::info!( diff --git a/pallets/subtensor/src/staking/remove_stake.rs b/pallets/subtensor/src/staking/remove_stake.rs index 5b3ed3390..fd6fe8191 100644 --- a/pallets/subtensor/src/staking/remove_stake.rs +++ b/pallets/subtensor/src/staking/remove_stake.rs @@ -66,7 +66,7 @@ impl<T: Config> Pallet<T> { ); // 6. Swap the alpba to tao and update counters for this subnet. - let fee = DefaultMinStake::<T>::get(); + let fee = DefaultStakingFee::<T>::get(); let tao_unstaked: u64 = Self::unstake_from_subnet(&hotkey, &coldkey, netuid, alpha_unstaked, fee); @@ -117,7 +117,7 @@ impl<T: Config> Pallet<T> { origin: T::RuntimeOrigin, hotkey: T::AccountId, ) -> dispatch::DispatchResult { - let fee = DefaultMinStake::<T>::get(); + let fee = DefaultStakingFee::<T>::get(); // 1. We check the transaction is signed by the caller and retrieve the T::AccountId coldkey information. let coldkey = ensure_signed(origin)?; @@ -185,7 +185,7 @@ impl<T: Config> Pallet<T> { origin: T::RuntimeOrigin, hotkey: T::AccountId, ) -> dispatch::DispatchResult { - let fee = DefaultMinStake::<T>::get(); + let fee = DefaultStakingFee::<T>::get(); // 1. We check the transaction is signed by the caller and retrieve the T::AccountId coldkey information. let coldkey = ensure_signed(origin)?; diff --git a/pallets/subtensor/src/tests/epoch.rs b/pallets/subtensor/src/tests/epoch.rs index 43ac6b98c..1d3bf8fc8 100644 --- a/pallets/subtensor/src/tests/epoch.rs +++ b/pallets/subtensor/src/tests/epoch.rs @@ -1505,7 +1505,7 @@ fn test_set_alpha_disabled() { signer.clone(), hotkey, netuid, - DefaultMinStake::<Test>::get() + DefaultMinStake::<Test>::get() + DefaultStakingFee::<Test>::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, - DefaultMinStake::<Test>::get() + DefaultMinStake::<Test>::get() + DefaultStakingFee::<Test>::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 150fa1201..4061f07e7 100644 --- a/pallets/subtensor/src/tests/move_stake.rs +++ b/pallets/subtensor/src/tests/move_stake.rs @@ -17,7 +17,7 @@ fn test_do_move_success() { let origin_hotkey = U256::from(2); let destination_hotkey = U256::from(3); let stake_amount = DefaultMinStake::<Test>::get() * 10; - let fee = DefaultMinStake::<Test>::get(); + let fee = DefaultStakingFee::<Test>::get(); // Set up initial stake SubtensorModule::create_account_if_non_existent(&coldkey, &origin_hotkey); @@ -74,7 +74,7 @@ fn test_do_move_different_subnets() { let origin_hotkey = U256::from(2); let destination_hotkey = U256::from(3); let stake_amount = DefaultMinStake::<Test>::get() * 10; - let fee = DefaultMinStake::<Test>::get(); + let fee = DefaultStakingFee::<Test>::get(); // Set up initial stake and subnets SubtensorModule::create_account_if_non_existent(&coldkey, &origin_hotkey); @@ -288,7 +288,7 @@ fn test_do_move_all_stake() { let origin_hotkey = U256::from(2); let destination_hotkey = U256::from(3); let stake_amount = DefaultMinStake::<Test>::get() * 10; - let fee = DefaultMinStake::<Test>::get(); + let fee = DefaultStakingFee::<Test>::get(); // Set up initial stake SubtensorModule::stake_into_subnet(&origin_hotkey, &coldkey, netuid, stake_amount, fee); @@ -341,7 +341,7 @@ fn test_do_move_half_stake() { let origin_hotkey = U256::from(2); let destination_hotkey = U256::from(3); let stake_amount = DefaultMinStake::<Test>::get() * 10; - let fee = DefaultMinStake::<Test>::get(); + let fee = DefaultStakingFee::<Test>::get(); // Set up initial stake SubtensorModule::stake_into_subnet(&origin_hotkey, &coldkey, netuid, stake_amount, fee); @@ -398,7 +398,7 @@ fn test_do_move_partial_stake() { let origin_hotkey = U256::from(2); let destination_hotkey = U256::from(3); let total_stake = DefaultMinStake::<Test>::get() * 10; - let fee = DefaultMinStake::<Test>::get(); + let fee = DefaultStakingFee::<Test>::get(); // Set up initial stake SubtensorModule::stake_into_subnet(&origin_hotkey, &coldkey, netuid, total_stake, fee); @@ -454,7 +454,7 @@ fn test_do_move_multiple_times() { let hotkey1 = U256::from(2); let hotkey2 = U256::from(3); let initial_stake = DefaultMinStake::<Test>::get() * 10; - let fee = DefaultMinStake::<Test>::get(); + let fee = DefaultStakingFee::<Test>::get(); // Set up initial stake SubtensorModule::create_account_if_non_existent(&coldkey, &hotkey1); @@ -570,7 +570,7 @@ fn test_do_move_same_hotkey() { let coldkey = U256::from(1); let hotkey = U256::from(2); let stake_amount = DefaultMinStake::<Test>::get() * 10; - let fee = DefaultMinStake::<Test>::get(); + let fee = DefaultStakingFee::<Test>::get(); // Set up initial stake SubtensorModule::create_account_if_non_existent(&coldkey, &hotkey); @@ -610,7 +610,7 @@ fn test_do_move_event_emission() { let origin_hotkey = U256::from(2); let destination_hotkey = U256::from(3); let stake_amount = DefaultMinStake::<Test>::get() * 10; - let fee = DefaultMinStake::<Test>::get(); + let fee = DefaultStakingFee::<Test>::get(); // Set up initial stake SubtensorModule::create_account_if_non_existent(&coldkey, &origin_hotkey); @@ -662,7 +662,7 @@ fn test_do_move_storage_updates() { let origin_hotkey = U256::from(2); let destination_hotkey = U256::from(3); let stake_amount = DefaultMinStake::<Test>::get() * 10; - let fee = DefaultMinStake::<Test>::get(); + let fee = DefaultStakingFee::<Test>::get(); // Set up initial stake SubtensorModule::stake_into_subnet( @@ -775,7 +775,7 @@ fn test_moving_too_little_fails() { let hotkey_account_id = U256::from(533453); let coldkey_account_id = U256::from(55453); let amount = DefaultMinStake::<Test>::get(); - let fee = DefaultMinStake::<Test>::get(); + let fee = DefaultStakingFee::<Test>::get(); //add network let netuid: u16 = add_dynamic_network(&hotkey_account_id, &coldkey_account_id); @@ -813,7 +813,7 @@ fn test_do_transfer_success() { 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 fee = DefaultMinStake::<Test>::get(); + let fee = DefaultStakingFee::<Test>::get(); // 2. Define the origin coldkey, destination coldkey, and hotkey to be used. let origin_coldkey = U256::from(1); @@ -952,7 +952,7 @@ fn test_do_transfer_wrong_origin() { let destination_coldkey = U256::from(2); let hotkey = U256::from(3); let stake_amount = DefaultMinStake::<Test>::get() * 10; - let fee = DefaultMinStake::<Test>::get(); + let fee = DefaultStakingFee::<Test>::get(); SubtensorModule::create_account_if_non_existent(&origin_coldkey, &hotkey); SubtensorModule::add_balance_to_coldkey_account(&origin_coldkey, stake_amount + fee); @@ -1015,7 +1015,7 @@ fn test_do_transfer_different_subnets() { let destination_coldkey = U256::from(2); let hotkey = U256::from(3); let stake_amount = DefaultMinStake::<Test>::get() * 10; - let fee = DefaultMinStake::<Test>::get(); + let fee = DefaultStakingFee::<Test>::get(); // 3. Create accounts if needed. SubtensorModule::create_account_if_non_existent(&origin_coldkey, &hotkey); @@ -1083,9 +1083,10 @@ fn test_do_swap_success() { let coldkey = U256::from(1); let hotkey = U256::from(2); let stake_amount = DefaultMinStake::<Test>::get() * 10; + let fee = DefaultStakingFee::<Test>::get(); SubtensorModule::create_account_if_non_existent(&coldkey, &hotkey); - SubtensorModule::stake_into_subnet(&hotkey, &coldkey, origin_netuid, stake_amount); + SubtensorModule::stake_into_subnet(&hotkey, &coldkey, origin_netuid, stake_amount, 0); let alpha_before = SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( &hotkey, &coldkey, @@ -1114,7 +1115,11 @@ fn test_do_swap_success() { &coldkey, destination_netuid, ); - assert_abs_diff_eq!(alpha_after, stake_amount, epsilon = stake_amount / 1000); + assert_abs_diff_eq!( + alpha_after, + stake_amount - fee, + epsilon = stake_amount / 1000 + ); }); } @@ -1180,7 +1185,7 @@ fn test_do_swap_insufficient_stake() { let attempted_swap = stake_amount * 2; SubtensorModule::create_account_if_non_existent(&coldkey, &hotkey); - SubtensorModule::stake_into_subnet(&hotkey, &coldkey, netuid1, stake_amount); + SubtensorModule::stake_into_subnet(&hotkey, &coldkey, netuid1, stake_amount, 0); assert_noop!( SubtensorModule::do_swap_stake( @@ -1209,7 +1214,7 @@ fn test_do_swap_wrong_origin() { let stake_amount = 100_000; SubtensorModule::create_account_if_non_existent(&real_coldkey, &hotkey); - SubtensorModule::stake_into_subnet(&hotkey, &real_coldkey, netuid1, stake_amount); + SubtensorModule::stake_into_subnet(&hotkey, &real_coldkey, netuid1, stake_amount, 0); assert_noop!( SubtensorModule::do_swap_stake( @@ -1237,7 +1242,7 @@ fn test_do_swap_minimum_stake_check() { let swap_amount = 1; SubtensorModule::create_account_if_non_existent(&coldkey, &hotkey); - SubtensorModule::stake_into_subnet(&hotkey, &coldkey, netuid, total_stake); + SubtensorModule::stake_into_subnet(&hotkey, &coldkey, netuid, total_stake, 0); assert_err!( SubtensorModule::do_swap_stake( @@ -1262,9 +1267,10 @@ fn test_do_swap_same_subnet() { let coldkey = U256::from(1); let hotkey = U256::from(2); let stake_amount = DefaultMinStake::<Test>::get() * 10; + let fee = DefaultStakingFee::<Test>::get(); SubtensorModule::create_account_if_non_existent(&coldkey, &hotkey); - SubtensorModule::stake_into_subnet(&hotkey, &coldkey, netuid, stake_amount); + SubtensorModule::stake_into_subnet(&hotkey, &coldkey, netuid, stake_amount, 0); let alpha_before = SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet(&hotkey, &coldkey, netuid); @@ -1279,7 +1285,11 @@ fn test_do_swap_same_subnet() { let alpha_after = SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet(&hotkey, &coldkey, netuid); - assert_abs_diff_eq!(alpha_after, alpha_before, epsilon = 5); + assert_abs_diff_eq!( + alpha_after, + alpha_before - fee, + epsilon = alpha_after / 1000 + ); }); } @@ -1294,9 +1304,10 @@ fn test_do_swap_partial_stake() { let coldkey = U256::from(1); let hotkey = U256::from(2); let total_stake = DefaultMinStake::<Test>::get() * 10; + let fee = DefaultStakingFee::<Test>::get(); SubtensorModule::create_account_if_non_existent(&coldkey, &hotkey); - SubtensorModule::stake_into_subnet(&hotkey, &coldkey, origin_netuid, total_stake); + SubtensorModule::stake_into_subnet(&hotkey, &coldkey, origin_netuid, total_stake, 0); let swap_amount = total_stake / 2; assert_ok!(SubtensorModule::do_swap_stake( @@ -1322,7 +1333,7 @@ fn test_do_swap_partial_stake() { &coldkey, destination_netuid ), - swap_amount, + swap_amount - fee, epsilon = total_stake / 1000 ); }); @@ -1339,9 +1350,10 @@ fn test_do_swap_storage_updates() { let coldkey = U256::from(1); let hotkey = U256::from(2); let stake_amount = DefaultMinStake::<Test>::get() * 10; + let fee = DefaultStakingFee::<Test>::get(); SubtensorModule::create_account_if_non_existent(&coldkey, &hotkey); - SubtensorModule::stake_into_subnet(&hotkey, &coldkey, origin_netuid, stake_amount); + SubtensorModule::stake_into_subnet(&hotkey, &coldkey, origin_netuid, stake_amount, 0); let alpha = SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( &hotkey, @@ -1371,8 +1383,8 @@ fn test_do_swap_storage_updates() { &coldkey, destination_netuid ), - alpha, - epsilon = 5 + alpha - fee, + epsilon = alpha / 1000 ); }); } @@ -1388,9 +1400,10 @@ fn test_do_swap_multiple_times() { let coldkey = U256::from(1); let hotkey = U256::from(2); let initial_stake = DefaultMinStake::<Test>::get() * 10; + let fee = DefaultStakingFee::<Test>::get(); SubtensorModule::create_account_if_non_existent(&coldkey, &hotkey); - SubtensorModule::stake_into_subnet(&hotkey, &coldkey, netuid1, initial_stake); + SubtensorModule::stake_into_subnet(&hotkey, &coldkey, netuid1, initial_stake, 0); for _ in 0..3 { let alpha1 = SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( @@ -1425,7 +1438,7 @@ fn test_do_swap_multiple_times() { SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet(&hotkey, &coldkey, netuid2); assert_abs_diff_eq!( final_stake_netuid1, - initial_stake, + initial_stake - 6 * fee, epsilon = initial_stake / 1000 ); assert_eq!(final_stake_netuid2, 0); diff --git a/pallets/subtensor/src/tests/senate.rs b/pallets/subtensor/src/tests/senate.rs index 01dfc17d7..17f478b9c 100644 --- a/pallets/subtensor/src/tests/senate.rs +++ b/pallets/subtensor/src/tests/senate.rs @@ -67,7 +67,7 @@ fn test_senate_join_works() { let burn_cost = 1000; let coldkey_account_id = U256::from(667); // Neighbour of the beast, har har let stake = DefaultMinStake::<Test>::get() * 100; - let fee = DefaultMinStake::<Test>::get(); + let fee = DefaultStakingFee::<Test>::get(); //add network SubtensorModule::set_burn(netuid, burn_cost); @@ -141,7 +141,7 @@ fn test_senate_vote_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 fee = DefaultMinStake::<Test>::get(); + let fee = DefaultStakingFee::<Test>::get(); //add network SubtensorModule::set_burn(netuid, burn_cost); @@ -315,7 +315,7 @@ fn test_senate_leave_works() { let burn_cost = 1000; let coldkey_account_id = U256::from(667); // Neighbour of the beast, har har let stake = DefaultMinStake::<Test>::get() * 10; - let fee = DefaultMinStake::<Test>::get(); + let fee = DefaultStakingFee::<Test>::get(); //add network SubtensorModule::set_burn(netuid, burn_cost); @@ -390,7 +390,7 @@ fn test_senate_leave_vote_removal() { let coldkey_account_id = U256::from(667); // Neighbour of the beast, har har let coldkey_origin = <<Test as Config>::RuntimeOrigin>::signed(coldkey_account_id); let stake = DefaultMinStake::<Test>::get() * 10; - let fee = DefaultMinStake::<Test>::get(); + let fee = DefaultStakingFee::<Test>::get(); //add network SubtensorModule::set_burn(netuid, burn_cost); @@ -532,7 +532,7 @@ fn test_senate_not_leave_when_stake_removed() { 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 fee = DefaultMinStake::<Test>::get(); + let fee = DefaultStakingFee::<Test>::get(); //add network SubtensorModule::set_burn(netuid, burn_cost); @@ -691,10 +691,11 @@ fn test_adjust_senate_events() { let burn_cost = 1000; let coldkey_account_id = U256::from(667); let root_netuid = SubtensorModule::get_root_netuid(); - let fee = DefaultMinStake::<Test>::get(); + let fee = DefaultStakingFee::<Test>::get(); let max_senate_size: u16 = SenateMaxMembers::get() as u16; - let stake_threshold: u64 = DefaultMinStake::<Test>::get(); // Give this much to every senator + let stake_threshold: u64 = + DefaultMinStake::<Test>::get() + DefaultStakingFee::<Test>::get(); // Give this much to every senator // We will be registering MaxMembers hotkeys and two more to try a replace let balance_to_add = DefaultMinStake::<Test>::get() * 10 @@ -761,13 +762,14 @@ fn test_adjust_senate_events() { coldkey_account_id ); // Add/delegate enough stake to join the senate + // +1 to be above hotkey_account_id assert_ok!(SubtensorModule::add_stake( <<Test as Config>::RuntimeOrigin>::signed(coldkey_account_id), new_hotkey_account_id, netuid, stake_threshold + 1 + i as u64 // Increasing with i to make them ordered - )); // +1 to be above hotkey_account_id - // Join senate + )); + // Join senate assert_ok!(SubtensorModule::root_register( <<Test as Config>::RuntimeOrigin>::signed(coldkey_account_id), new_hotkey_account_id diff --git a/pallets/subtensor/src/tests/staking.rs b/pallets/subtensor/src/tests/staking.rs index ef5ac5f4a..f57fb4ebe 100644 --- a/pallets/subtensor/src/tests/staking.rs +++ b/pallets/subtensor/src/tests/staking.rs @@ -42,7 +42,7 @@ fn test_add_stake_ok_no_emission() { let hotkey_account_id = U256::from(533453); let coldkey_account_id = U256::from(55453); let amount = DefaultMinStake::<Test>::get() * 10; - let fee = DefaultMinStake::<Test>::get(); + let fee = DefaultStakingFee::<Test>::get(); //add network let netuid: u16 = add_dynamic_network(&hotkey_account_id, &coldkey_account_id); @@ -351,7 +351,7 @@ fn test_remove_stake_ok_no_emission() { let coldkey_account_id = U256::from(4343); let hotkey_account_id = U256::from(4968585); let amount = DefaultMinStake::<Test>::get() * 10; - let fee = DefaultMinStake::<Test>::get(); + let fee = DefaultStakingFee::<Test>::get(); let netuid: u16 = add_dynamic_network(&subnet_owner_hotkey, &subnet_owner_coldkey); register_ok_neuron(netuid, hotkey_account_id, coldkey_account_id, 192213123); @@ -508,7 +508,7 @@ fn test_remove_stake_total_balance_no_change() { let hotkey_account_id = U256::from(571337); let coldkey_account_id = U256::from(71337); let amount = DefaultMinStake::<Test>::get() * 10; - let fee = DefaultMinStake::<Test>::get(); + let fee = DefaultStakingFee::<Test>::get(); let netuid: u16 = add_dynamic_network(&subnet_owner_hotkey, &subnet_owner_coldkey); register_ok_neuron(netuid, hotkey_account_id, coldkey_account_id, 192213123); @@ -567,7 +567,7 @@ fn test_remove_stake_total_issuance_no_change() { let coldkey_account_id = U256::from(81337); let amount = DefaultMinStake::<Test>::get() * 10; let netuid: u16 = add_dynamic_network(&subnet_owner_hotkey, &subnet_owner_coldkey); - let fee = DefaultMinStake::<Test>::get(); + let fee = DefaultStakingFee::<Test>::get(); register_ok_neuron(netuid, hotkey_account_id, coldkey_account_id, 192213123); // Give it some $$$ in his coldkey balance @@ -1609,7 +1609,7 @@ fn test_get_total_delegated_stake_after_unstaking() { let unstake_amount = DefaultMinStake::<Test>::get() * 5; let existential_deposit = ExistentialDeposit::get(); let netuid = add_dynamic_network(&subnet_owner_hotkey, &subnet_owner_coldkey); - let fee = DefaultMinStake::<Test>::get(); + let fee = DefaultStakingFee::<Test>::get(); register_ok_neuron(netuid, delegate_hotkey, delegate_coldkey, 0); @@ -1697,7 +1697,7 @@ fn test_get_total_delegated_stake_single_delegator() { let stake_amount = DefaultMinStake::<Test>::get() * 10 - 1; let existential_deposit = ExistentialDeposit::get(); let netuid = add_dynamic_network(&subnet_owner_hotkey, &subnet_owner_coldkey); - let fee = DefaultMinStake::<Test>::get(); + let fee = DefaultStakingFee::<Test>::get(); register_ok_neuron(netuid, delegate_hotkey, delegate_coldkey, 0); @@ -1755,7 +1755,7 @@ fn test_get_alpha_share_stake_multiple_delegators() { let existential_deposit = 2; let stake1 = DefaultMinStake::<Test>::get() * 10; let stake2 = DefaultMinStake::<Test>::get() * 10 - 1; - let fee = DefaultMinStake::<Test>::get(); + let fee = DefaultStakingFee::<Test>::get(); let netuid = add_dynamic_network(&subnet_owner_hotkey, &subnet_owner_coldkey); register_ok_neuron(netuid, hotkey1, coldkey1, 0); @@ -1814,7 +1814,7 @@ fn test_get_total_delegated_stake_exclude_owner_stake() { let delegator = U256::from(3); let owner_stake = DefaultMinStake::<Test>::get() * 10; let delegator_stake = DefaultMinStake::<Test>::get() * 10 - 1; - let fee = DefaultMinStake::<Test>::get(); + let fee = DefaultStakingFee::<Test>::get(); let netuid = add_dynamic_network(&delegate_hotkey, &delegate_coldkey); @@ -1975,7 +1975,7 @@ fn test_add_stake_fee_goes_to_subnet_tao() { let coldkey = U256::from(3); let existential_deposit = ExistentialDeposit::get(); let tao_to_stake = DefaultMinStake::<Test>::get() * 10; - let fee = DefaultMinStake::<Test>::get(); + let fee = DefaultStakingFee::<Test>::get(); let netuid = add_dynamic_network(&subnet_owner_hotkey, &subnet_owner_coldkey); SubtensorModule::create_account_if_non_existent(&coldkey, &hotkey); @@ -2021,7 +2021,7 @@ fn test_remove_stake_fee_goes_to_subnet_tao() { let hotkey = U256::from(2); let coldkey = U256::from(3); let tao_to_stake = DefaultMinStake::<Test>::get() * 10; - let fee = DefaultMinStake::<Test>::get(); + let fee = DefaultStakingFee::<Test>::get(); let netuid = add_dynamic_network(&subnet_owner_hotkey, &subnet_owner_coldkey); SubtensorModule::create_account_if_non_existent(&coldkey, &hotkey); @@ -2074,7 +2074,7 @@ fn test_stake_below_min_validate() { let subnet_owner_hotkey = U256::from(1002); let hotkey = U256::from(2); let coldkey = U256::from(3); - let amount_staked = DefaultMinStake::<Test>::get() - 1; + let amount_staked = DefaultMinStake::<Test>::get() + DefaultStakingFee::<Test>::get() - 1; let netuid = add_dynamic_network(&subnet_owner_hotkey, &subnet_owner_coldkey); SubtensorModule::create_account_if_non_existent(&coldkey, &hotkey); @@ -2101,7 +2101,7 @@ fn test_stake_below_min_validate() { ); // Increase the stake to be equal to the minimum, but leave the balance low - let amount_staked = DefaultMinStake::<Test>::get(); + let amount_staked = DefaultMinStake::<Test>::get() + DefaultStakingFee::<Test>::get(); let call_2 = RuntimeCall::SubtensorModule(SubtensorCall::add_stake { hotkey, netuid, @@ -2114,7 +2114,7 @@ fn test_stake_below_min_validate() { // Still doesn't pass assert_err!( result_low_balance, - crate::TransactionValidityError::Invalid(crate::InvalidTransaction::Custom(1)) + crate::TransactionValidityError::Invalid(crate::InvalidTransaction::Custom(2)) ); // Increase the coldkey balance to match the minimum diff --git a/pallets/subtensor/src/tests/swap_coldkey.rs b/pallets/subtensor/src/tests/swap_coldkey.rs index 45b3ba6e6..f21af56cf 100644 --- a/pallets/subtensor/src/tests/swap_coldkey.rs +++ b/pallets/subtensor/src/tests/swap_coldkey.rs @@ -366,7 +366,7 @@ fn test_swap_with_max_values() { let netuid2 = 2u16; let stake = 10_000; let max_stake = 21_000_000_000_000_000; // 21 Million TAO; max possible balance. - let fee = DefaultMinStake::<Test>::get(); + let fee = DefaultStakingFee::<Test>::get(); // Add a network add_network(netuid, 1, 0); @@ -435,7 +435,7 @@ fn test_swap_with_non_existent_new_coldkey() { let hotkey = U256::from(3); let stake = DefaultMinStake::<Test>::get() * 10; let netuid = 1u16; - let fee = DefaultMinStake::<Test>::get(); + let fee = DefaultStakingFee::<Test>::get(); add_network(netuid, 1, 0); register_ok_neuron(netuid, hotkey, old_coldkey, 1001000); @@ -528,7 +528,7 @@ fn test_swap_concurrent_modifications() { let netuid: u16 = 1; let initial_stake = 1_000_000_000_000; let additional_stake = 500_000_000_000; - let fee = DefaultMinStake::<Test>::get(); + let fee = DefaultStakingFee::<Test>::get(); // Setup initial state add_network(netuid, 1, 1); @@ -806,7 +806,7 @@ fn test_swap_stake_for_coldkey() { let stake_amount3 = DefaultMinStake::<Test>::get() * 30; let total_stake = stake_amount1 + stake_amount2; let mut weight = Weight::zero(); - let fee = DefaultMinStake::<Test>::get(); + let fee = DefaultStakingFee::<Test>::get(); // Setup initial state // Add a network @@ -962,7 +962,7 @@ fn test_swap_staking_hotkeys_for_coldkey() { let stake_amount2 = DefaultMinStake::<Test>::get() * 20; let total_stake = stake_amount1 + stake_amount2; let mut weight = Weight::zero(); - let fee = DefaultMinStake::<Test>::get(); + let fee = DefaultStakingFee::<Test>::get(); // Setup initial state // Add a network @@ -1034,7 +1034,7 @@ fn test_swap_delegated_stake_for_coldkey() { let stake_amount2 = DefaultMinStake::<Test>::get() * 20; let mut weight = Weight::zero(); let netuid = 1u16; - let fee = DefaultMinStake::<Test>::get(); + let fee = DefaultStakingFee::<Test>::get(); // Setup initial state add_network(netuid, 1, 0); @@ -1595,7 +1595,7 @@ fn test_coldkey_delegations() { let netuid = 0u16; // Stake to 0 let netuid2 = 1u16; // Stake to 1 let stake = DefaultMinStake::<Test>::get() * 10; - let fee = DefaultMinStake::<Test>::get(); + let fee = DefaultStakingFee::<Test>::get(); add_network(netuid, 13, 0); // root add_network(netuid2, 13, 0); diff --git a/pallets/subtensor/src/tests/swap_hotkey.rs b/pallets/subtensor/src/tests/swap_hotkey.rs index d35dd6d56..b11901e73 100644 --- a/pallets/subtensor/src/tests/swap_hotkey.rs +++ b/pallets/subtensor/src/tests/swap_hotkey.rs @@ -66,7 +66,7 @@ fn test_swap_total_hotkey_stake() { let coldkey = U256::from(3); let amount = DefaultMinStake::<Test>::get() * 10; let mut weight = Weight::zero(); - let fee = DefaultMinStake::<Test>::get(); + let fee = DefaultStakingFee::<Test>::get(); //add network let netuid: u16 = add_dynamic_network(&old_hotkey, &coldkey); diff --git a/pallets/subtensor/src/tests/weights.rs b/pallets/subtensor/src/tests/weights.rs index 10c829870..9a89ec76e 100644 --- a/pallets/subtensor/src/tests/weights.rs +++ b/pallets/subtensor/src/tests/weights.rs @@ -69,7 +69,7 @@ fn test_set_rootweights_validate() { let coldkey = U256::from(0); let hotkey: U256 = U256::from(1); // Add the hotkey field assert_ne!(hotkey, coldkey); // Ensure hotkey is NOT the same as coldkey !!! - let fee = DefaultMinStake::<Test>::get(); + let fee = DefaultStakingFee::<Test>::get(); let who = coldkey; // The coldkey signs this transaction @@ -184,7 +184,7 @@ fn test_commit_weights_validate() { let coldkey = U256::from(0); let hotkey: U256 = U256::from(1); // Add the hotkey field assert_ne!(hotkey, coldkey); // Ensure hotkey is NOT the same as coldkey !!! - let fee = DefaultMinStake::<Test>::get(); + let fee = DefaultStakingFee::<Test>::get(); let who = hotkey; // The hotkey signs this transaction @@ -294,7 +294,7 @@ fn test_set_weights_validate() { let coldkey = U256::from(0); let hotkey: U256 = U256::from(1); assert_ne!(hotkey, coldkey); - let fee = DefaultMinStake::<Test>::get(); + let fee = DefaultStakingFee::<Test>::get(); let who = hotkey; // The hotkey signs this transaction @@ -367,7 +367,7 @@ fn test_reveal_weights_validate() { let coldkey = U256::from(0); let hotkey: U256 = U256::from(1); // Add the hotkey field assert_ne!(hotkey, coldkey); // Ensure hotkey is NOT the same as coldkey !!! - let fee = DefaultMinStake::<Test>::get(); + let fee = DefaultStakingFee::<Test>::get(); let who = hotkey; // The hotkey signs this transaction From 1a34d3318c7fb341bbb44477338c74c4883f0684 Mon Sep 17 00:00:00 2001 From: open-junius <zhou@opentensor.dev> Date: Thu, 23 Jan 2025 09:49:36 +0800 Subject: [PATCH 042/145] refactor helper --- runtime/src/precompiles/mod.rs | 11 +++++++++++ runtime/src/precompiles/subnet.rs | 9 +++------ 2 files changed, 14 insertions(+), 6 deletions(-) diff --git a/runtime/src/precompiles/mod.rs b/runtime/src/precompiles/mod.rs index 3d2c19ae9..3c62d4b8c 100644 --- a/runtime/src/precompiles/mod.rs +++ b/runtime/src/precompiles/mod.rs @@ -232,3 +232,14 @@ fn dispatch( }), } } + +pub fn get_pubkey(data: &[u8]) -> Result<(AccountId32, Vec<u8>), PrecompileFailure> { + let mut pubkey = [0u8; 32]; + pubkey.copy_from_slice(data.get_slice(0, 32)?); + + Ok(( + pubkey.into(), + data.get(4..) + .map_or_else(vec::Vec::new, |slice| slice.to_vec()), + )) +} diff --git a/runtime/src/precompiles/subnet.rs b/runtime/src/precompiles/subnet.rs index c1827f77a..9c9fd859f 100644 --- a/runtime/src/precompiles/subnet.rs +++ b/runtime/src/precompiles/subnet.rs @@ -1,4 +1,4 @@ -use crate::precompiles::{dispatch, get_method_id, get_slice}; +use crate::precompiles::{dispatch, get_method_id, get_pubkey, get_slice}; use crate::{Runtime, RuntimeCall}; use pallet_evm::{ExitError, PrecompileFailure, PrecompileHandle, PrecompileResult}; use sp_std::vec; @@ -42,8 +42,7 @@ impl SubnetPrecompile { fn register_network(handle: &mut impl PrecompileHandle, data: &[u8]) -> PrecompileResult { let call = match data.len() { 32 => { - let mut hotkey = [0u8; 32]; - hotkey.copy_from_slice(get_slice(data, 0, 32)?); + let (hotkey, _) = get_pubkey(data); RuntimeCall::SubtensorModule( pallet_subtensor::Call::<Runtime>::register_network_with_identity { @@ -85,11 +84,9 @@ impl SubnetPrecompile { fn parse_register_network_parameters( data: &[u8], ) -> Result<([u8; 32], vec::Vec<u8>, vec::Vec<u8>, vec::Vec<u8>), PrecompileFailure> { - let mut pubkey = [0u8; 32]; - pubkey.copy_from_slice(get_slice(data, 0, 32)?); + let (pubkey, _) = get_pubkey(data)?; let mut buf = [0_u8; 4]; - // get all start point for three data items: name, repo and contact buf.copy_from_slice(get_slice(data, 60, 64)?); let subnet_name_start: usize = u32::from_be_bytes(buf) as usize; From 130d09fb7d6fba6d89e38b73c788bdf8ffaa8b62 Mon Sep 17 00:00:00 2001 From: camfairchild <cameron@opentensor.dev> Date: Thu, 23 Jan 2025 10:01:32 -0500 Subject: [PATCH 043/145] refactor loops to be less confusing --- .../subtensor/src/coinbase/run_coinbase.rs | 222 ++++++++++-------- 1 file changed, 123 insertions(+), 99 deletions(-) diff --git a/pallets/subtensor/src/coinbase/run_coinbase.rs b/pallets/subtensor/src/coinbase/run_coinbase.rs index 7e73ea8eb..a8af8322c 100644 --- a/pallets/subtensor/src/coinbase/run_coinbase.rs +++ b/pallets/subtensor/src/coinbase/run_coinbase.rs @@ -308,7 +308,7 @@ impl<T: Config> Pallet<T> { owner_cut ); - // Run the epoch() --> hotkey emission. + // === 1. Run the epoch() --> hotkey emission. // Needs to run on the full emission to the subnet. let hotkey_emission: Vec<(T::AccountId, u64, u64)> = Self::epoch( netuid, @@ -320,15 +320,16 @@ impl<T: Config> Pallet<T> { hotkey_emission ); - // Pay out the hotkey alpha dividends. - // First clear the netuid from HotkeyDividends - let mut total_root_alpha_divs: u64 = 0; - let mut root_alpha_divs: BTreeMap<T::AccountId, u64> = BTreeMap::new(); + // === 2. Calculate the dividend distributions using the current stake. + // Clear maps let _ = AlphaDividendsPerSubnet::<T>::clear_prefix(netuid, u32::MAX, None); + let _ = TaoDividendsPerSubnet::<T>::clear_prefix(netuid, u32::MAX, None); + // Initialize maps for parent-child OR miner dividend distributions. let mut dividends_to_distribute: Vec<(T::AccountId, Vec<(T::AccountId, u64)>)> = Vec::new(); let mut mining_incentive_to_distribute: Vec<(T::AccountId, u64)> = Vec::new(); + // 2.1 --- Get dividend distribution from parent-child and miner distributions. for (hotkey, incentive, dividends) in hotkey_emission { log::debug!( "Processing hotkey {:?} with incentive {:?} and dividends {:?}", @@ -354,18 +355,23 @@ impl<T: Config> Pallet<T> { dividends_to_distribute.push((hotkey.clone(), dividend_tuples)); } - // Calculate the validator take and root alpha divs using the alpha divs. + // Initialize maps for dividend calculations. + let mut root_divs: BTreeMap<T::AccountId, u64> = BTreeMap::new(); + let mut alpha_divs: BTreeMap<T::AccountId, u64> = BTreeMap::new(); + let mut validator_takes: BTreeMap<T::AccountId, u64> = BTreeMap::new(); + + // 2.2 --- Calculate the validator_take, alpha_divs, and root_divs using above dividend tuples. for (hotkey, dividend_tuples) in dividends_to_distribute.iter() { - // Calculate the dividends to hotkeys based on the local vs root proportion. + // Calculate the proportion of root vs alpha divs for each hotkey using their stake. for (hotkey_j, divs_j) in dividend_tuples.iter() { log::debug!( - "Processing dividend for hotkey {:?} to hotkey {:?}: {:?}", + "Processing dividend for child-hotkey {:?} to parent-hotkey {:?}: {:?}", hotkey, hotkey_j, *divs_j ); - // Get the local alpha and root alpha. + // 2.1 --- Get the local alpha and root alpha. let hotkey_tao: I96F32 = I96F32::from_num(Self::get_stake_for_hotkey_on_subnet( hotkey_j, Self::get_root_netuid(), @@ -375,7 +381,7 @@ impl<T: Config> Pallet<T> { I96F32::from_num(Self::get_stake_for_hotkey_on_subnet(hotkey_j, netuid)); log::debug!("Hotkey tao for hotkey {:?} on root netuid: {:?}, hotkey tao as alpha: {:?}, hotkey alpha: {:?}", hotkey_j, hotkey_tao, hotkey_tao_as_alpha, hotkey_alpha); - // Compute alpha and root proportions. + // 2.2 --- Compute alpha and root proportions. let alpha_prop: I96F32 = hotkey_alpha .checked_div(hotkey_alpha.saturating_add(hotkey_tao_as_alpha)) .unwrap_or(I96F32::from_num(0.0)); @@ -388,7 +394,7 @@ impl<T: Config> Pallet<T> { root_prop ); - // Remove the hotkey take straight off the top. + // 2.3.1 --- Remove the hotkey take from both alpha and root divs. let take_prop: I96F32 = I96F32::from_num(Self::get_hotkey_take(hotkey_j)) .checked_div(I96F32::from_num(u16::MAX)) .unwrap_or(I96F32::from_num(0.0)); @@ -401,30 +407,57 @@ impl<T: Config> Pallet<T> { rem_divs_j ); - // Compute root dividends - let root_divs: I96F32 = rem_divs_j.saturating_mul(root_prop); + // 2.3.2 --- Store the validator take. + validator_takes + .entry(hotkey_j.clone()) + .and_modify(|e| *e = e.saturating_add(validator_take.to_num::<u64>())) + .or_insert(validator_take.to_num::<u64>()); + log::debug!( + "Stored validator take for hotkey {:?}: {:?}", + hotkey_j, + validator_take.to_num::<u64>() + ); + + // 2.4.1 --- Compute root dividends + let root_divs_j: I96F32 = rem_divs_j.saturating_mul(root_prop); + // 2.4.2 --- Compute alpha dividends + let alpha_divs_j: I96F32 = rem_divs_j.saturating_sub(root_divs_j); log::debug!( "Alpha dividends: {:?}, root dividends: {:?}", - rem_divs_j, - root_divs + alpha_divs_j, + root_divs_j ); - // Store the root-alpha divs under hotkey_j - root_alpha_divs + // 2.4.3 --- Store the root divs under hotkey_j + root_divs .entry(hotkey_j.clone()) - .and_modify(|e| *e = e.saturating_add(root_divs.to_num::<u64>())) - .or_insert(root_divs.to_num::<u64>()); - total_root_alpha_divs = - total_root_alpha_divs.saturating_add(root_divs.to_num::<u64>()); + .and_modify(|e| *e = e.saturating_add(root_divs_j.to_num::<u64>())) + .or_insert(root_divs_j.to_num::<u64>()); log::debug!( "Stored root alpha dividends for hotkey {:?}: {:?}", hotkey_j, - root_divs.to_num::<u64>() + root_divs_j.to_num::<u64>() + ); + + // 2.4.4 --- Store the alpha dividends + alpha_divs + .entry(hotkey_j.clone()) + .and_modify(|e| *e = e.saturating_add(alpha_divs_j.to_num::<u64>())) + .or_insert(alpha_divs_j.to_num::<u64>()); + log::debug!( + "Stored alpha dividends for hotkey {:?}: {:?}", + hotkey_j, + alpha_divs_j.to_num::<u64>() ); } } - // Check for existence of owner cold/hot pair and distribute emission directly to them. + let total_root_divs: u64 = root_divs.values().sum(); + + // === 3. Distribute the dividends to the hotkeys. + + // 3.1 --- Distribute owner cut. + // Check for existence of subnet owner cold/hot pair and distribute emission directly to them. if let Ok(owner_coldkey) = SubnetOwner::<T>::try_get(netuid) { if let Ok(owner_hotkey) = SubnetOwnerHotkey::<T>::try_get(netuid) { // Increase stake for both coldkey and hotkey on the subnet @@ -438,98 +471,82 @@ impl<T: Config> Pallet<T> { } } - // Distribute mining incentive. - for (hotkey, incentive) in mining_incentive_to_distribute { + // 3.2 --- Distribute mining incentive. + for (miner_j, incentive) in mining_incentive_to_distribute { // Distribute mining incentive immediately. Self::increase_stake_for_hotkey_and_coldkey_on_subnet( - &hotkey.clone(), - &Owner::<T>::get(hotkey.clone()), + &miner_j.clone(), + &Owner::<T>::get(miner_j.clone()), netuid, incentive, ); log::debug!( "Distributed mining incentive for hotkey {:?} on netuid {:?}: {:?}", - hotkey, + miner_j, netuid, incentive ); } - // Distribute validator take and alpha-dividends. - for (_hotkey, dividend_tuples) in dividends_to_distribute.iter() { - // Pay out dividends to hotkeys based on the local vs root proportion. - for (hotkey_j, divs_j) in dividend_tuples.iter() { - // Remove the hotkey take straight off the top. - let take_prop: I96F32 = I96F32::from_num(Self::get_hotkey_take(hotkey_j)) - .checked_div(I96F32::from_num(u16::MAX)) - .unwrap_or(I96F32::from_num(0.0)); - let validator_take: I96F32 = take_prop.saturating_mul(I96F32::from_num(*divs_j)); - let rem_divs_j: I96F32 = I96F32::from_num(*divs_j).saturating_sub(validator_take); - log::debug!( - "Validator take for hotkey {:?}: {:?}, remaining dividends: {:?}", - hotkey_j, - validator_take, - rem_divs_j - ); - - let possible_root_divs = *root_alpha_divs.get(hotkey_j).unwrap_or(&0); - let alpha_divs: I96F32 = - rem_divs_j.saturating_sub(I96F32::from_num(possible_root_divs)); - log::debug!( - "Removed root divs for hotkey {:?}: {:?}", - hotkey_j, - possible_root_divs - ); + // 3.3 --- Distribute validator take + for (validator_j, validator_take) in validator_takes { + // 3.3.1 --- Distribute validator take to the validator. + Self::increase_stake_for_hotkey_and_coldkey_on_subnet( + &validator_j.clone(), + &Owner::<T>::get(validator_j.clone()), + netuid, + validator_take, + ); + log::debug!( + "Distributed validator take for hotkey {:?} on netuid {:?}: {:?}", + validator_j, + netuid, + validator_take + ); - // Distribute validator take. - Self::increase_stake_for_hotkey_and_coldkey_on_subnet( - hotkey_j, - &Owner::<T>::get(hotkey_j.clone()), - netuid, - validator_take.to_num::<u64>(), - ); - log::debug!( - "Distributed validator take for hotkey {:?} on netuid {:?}: {:?}", - hotkey_j, - netuid, - validator_take.to_num::<u64>() - ); + // 3.3.2 --- Record dividends for this validator on this subnet. + AlphaDividendsPerSubnet::<T>::mutate(netuid, validator_j.clone(), |divs| { + *divs = divs.saturating_add(validator_take); + }); + log::debug!( + "Recorded dividends for validator {:?} on netuid {:?}: {:?}", + validator_j, + netuid, + validator_take + ); + } - // Distribute the alpha divs to the hotkey. - Self::increase_stake_for_hotkey_on_subnet( - hotkey_j, - netuid, - alpha_divs.to_num::<u64>(), - ); - log::debug!( - "Distributed alpha dividends for hotkey {:?} on netuid {:?}: {:?}", - hotkey_j, - netuid, - alpha_divs.to_num::<u64>() - ); + // 3.4 --- Distribute alpha divs + for (hotkey_j, alpha_divs_j) in alpha_divs { + // 3.4.1 --- Distribute alpha divs to the hotkey. + Self::increase_stake_for_hotkey_on_subnet(&hotkey_j.clone(), netuid, alpha_divs_j); + log::debug!( + "Distributed alpha dividends for hotkey {:?} on netuid {:?}: {:?}", + hotkey_j, + netuid, + alpha_divs_j + ); - // Record dividends for this hotkey on this subnet. - AlphaDividendsPerSubnet::<T>::mutate(netuid, hotkey_j.clone(), |divs| { - *divs = divs - .saturating_add(alpha_divs.to_num::<u64>()) - .saturating_add(validator_take.to_num::<u64>()); - }); - log::debug!( - "Recorded dividends for hotkey {:?} on netuid {:?}: {:?}", - hotkey_j, - netuid, - *divs_j - ); - } + // 3.4.2 --- Record dividends for this hotkey on this subnet. + AlphaDividendsPerSubnet::<T>::mutate(netuid, hotkey_j.clone(), |divs| { + *divs = divs.saturating_add(alpha_divs_j); + }); + log::debug!( + "Recorded dividends for hotkey {:?} on netuid {:?}: {:?}", + hotkey_j, + netuid, + alpha_divs_j + ); } + // 3.5 --- Distribute root divs // For all the root-alpha divs give this proportion of the swapped tao to the root participants. - let _ = TaoDividendsPerSubnet::<T>::clear_prefix(netuid, u32::MAX, None); - - for (hotkey_j, root_divs) in root_alpha_divs.iter() { - let proportion: I96F32 = I96F32::from_num(*root_divs) - .checked_div(I96F32::from_num(total_root_alpha_divs)) + for (hotkey_j, root_divs_j) in root_divs.iter() { + // 3.5.1 --- Calculate the proportion of root divs to pay out to this hotkey. + let proportion: I96F32 = I96F32::from_num(*root_divs_j) + .checked_div(I96F32::from_num(total_root_divs)) .unwrap_or(I96F32::from_num(0)); + // 3.5.2 --- Get the proportion of root divs from the pending root divs. let root_divs_to_pay: u64 = proportion .saturating_mul(I96F32::from_num(pending_root_divs)) .to_num::<u64>(); @@ -540,22 +557,29 @@ impl<T: Config> Pallet<T> { root_divs_to_pay ); - // Pay the tao to the hotkey on netuid 0 + // 3.5.3 --- Distribute the root divs to the hotkey on the root subnet. Self::increase_stake_for_hotkey_on_subnet( hotkey_j, Self::get_root_netuid(), root_divs_to_pay, ); log::debug!( - "Paid tao to hotkey {:?} on root netuid: {:?}", + "Paid tao to hotkey {:?} on root netuid from netuid {:?}: {:?}", hotkey_j, + netuid, root_divs_to_pay ); - // Record dividends for this hotkey on this subnet. + // 3.5.4 --- Record dividends for this hotkey on this subnet. TaoDividendsPerSubnet::<T>::mutate(netuid, hotkey_j.clone(), |divs| { *divs = divs.saturating_add(root_divs_to_pay); }); + log::debug!( + "Recorded dividends for hotkey {:?} on netuid {:?}: {:?}", + hotkey_j, + netuid, + root_divs_to_pay + ); } } From e797da95521577ddc610f508f31abd4f76a7ce55 Mon Sep 17 00:00:00 2001 From: Aliaksandr Tsurko <ales@opentensor.dev> Date: Thu, 23 Jan 2025 16:12:08 +0100 Subject: [PATCH 044/145] Fix gas estimation issues --- Cargo.lock | 45 +++++++++++ Cargo.toml | 3 +- node/src/chain_spec/localnet.rs | 13 ++- runtime/Cargo.toml | 2 + runtime/src/precompiles/balance_transfer.rs | 88 +++++++++++---------- runtime/src/precompiles/staking.rs | 42 +++++----- 6 files changed, 130 insertions(+), 63 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 964c583e1..e86ef3d69 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1067,6 +1067,12 @@ dependencies = [ "thiserror", ] +[[package]] +name = "case" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd6c0e7b807d60291f42f33f58480c0bfafe28ed08286446f45e463728cf9c1c" + [[package]] name = "cc" version = "1.1.24" @@ -5658,6 +5664,7 @@ dependencies = [ "pallet-transaction-payment-rpc-runtime-api", "pallet-utility", "parity-scale-codec", + "precompile-utils", "rand_chacha", "scale-info", "serde_json", @@ -7010,6 +7017,44 @@ dependencies = [ "zerocopy", ] +[[package]] +name = "precompile-utils" +version = "0.1.0" +source = "git+https://github.com/opentensor/frontier?rev=635bdac882#635bdac882333afed827053f31ef56ab739f7a2e" +dependencies = [ + "environmental", + "evm", + "fp-evm", + "frame-support", + "frame-system", + "hex", + "impl-trait-for-tuples", + "log", + "num_enum", + "pallet-evm", + "parity-scale-codec", + "precompile-utils-macro", + "sp-core", + "sp-io", + "sp-runtime", + "sp-weights", + "staging-xcm", +] + +[[package]] +name = "precompile-utils-macro" +version = "0.1.0" +source = "git+https://github.com/opentensor/frontier?rev=635bdac882#635bdac882333afed827053f31ef56ab739f7a2e" +dependencies = [ + "case", + "num_enum", + "prettyplease 0.2.22", + "proc-macro2", + "quote", + "sp-crypto-hashing 0.1.0 (git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-stable2409)", + "syn 1.0.109", +] + [[package]] name = "predicates" version = "2.1.5" diff --git a/Cargo.toml b/Cargo.toml index 3d5adb18a..0e723d6ae 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -180,9 +180,10 @@ fc-consensus = { git = "https://github.com/opentensor/frontier", rev = "635bdac8 fp-consensus = { git = "https://github.com/opentensor/frontier", rev = "635bdac882", default-features = false } fp-dynamic-fee = { git = "https://github.com/opentensor/frontier", rev = "635bdac882", default-features = false } fc-api = { git = "https://github.com/opentensor/frontier", rev = "635bdac882", default-features = false } -fc-rpc = { git = "https://github.com/opentensor/frontier", rev = "635bdac882", default-features = false } +fc-rpc = { git = "https://github.com/opentensor/frontier", rev = "635bdac882", default-features = false, features = ["rpc-binary-search-estimate"]} fc-rpc-core = { git = "https://github.com/opentensor/frontier", rev = "635bdac882", default-features = false } fc-mapping-sync = { git = "https://github.com/opentensor/frontier", rev = "635bdac882", default-features = false } +precompile-utils = { git = "https://github.com/opentensor/frontier", rev = "635bdac882", default-features = false } # Frontier FRAME pallet-base-fee = { git = "https://github.com/opentensor/frontier", rev = "635bdac882", default-features = false } diff --git a/node/src/chain_spec/localnet.rs b/node/src/chain_spec/localnet.rs index 7f5b3611c..4aa84f8ca 100644 --- a/node/src/chain_spec/localnet.rs +++ b/node/src/chain_spec/localnet.rs @@ -76,6 +76,17 @@ fn localnet_genesis( ( get_account_id_from_seed::<sr25519::Public>("Ferdie"), 2000000000000u128, + ), + // ETH + ( + // Alith - 0xf24FF3a9CF04c71Dbc94D0b566f7A27B94566cac + AccountId::from_str("5Fghzk1AJt88PeFEzuRfXzbPchiBbsVGTTXcdx599VdZzkTA").unwrap(), + 2000000000000u128, + ), + ( + // Baltathar - 0x3Cd0A705a2DC65e5b1E1205896BaA2be8A07c6e0 + AccountId::from_str("5GeqNhKWj1KG78VHzbmo3ZjZgUTrCuWeamdgiA114XHGdaEr").unwrap(), + 2000000000000u128, ), ]; @@ -120,7 +131,7 @@ fn localnet_genesis( "senateMembers": { "members": senate_members, }, - "evmChainId": { + "evmChainId": { "chainId": 42, }, }) diff --git a/runtime/Cargo.toml b/runtime/Cargo.toml index cdb8fd52f..0d44e0907 100644 --- a/runtime/Cargo.toml +++ b/runtime/Cargo.toml @@ -98,6 +98,7 @@ pallet-commitments = { default-features = false, path = "../pallets/commitments" fp-evm = { workspace = true } fp-rpc = { workspace = true } fp-self-contained = { workspace = true } +precompile-utils = { workspace = true } # Frontier FRAME pallet-base-fee = { workspace = true } @@ -191,6 +192,7 @@ std = [ "fp-evm/std", "fp-rpc/std", "fp-self-contained/std", + "precompile-utils/std", # Frontier FRAME "pallet-base-fee/std", "pallet-dynamic-fee/std", diff --git a/runtime/src/precompiles/balance_transfer.rs b/runtime/src/precompiles/balance_transfer.rs index 99d911b02..7c534212d 100644 --- a/runtime/src/precompiles/balance_transfer.rs +++ b/runtime/src/precompiles/balance_transfer.rs @@ -3,16 +3,24 @@ use pallet_evm::{ BalanceConverter, ExitError, ExitSucceed, PrecompileFailure, PrecompileHandle, PrecompileOutput, PrecompileResult, }; +use precompile_utils::prelude::RuntimeHelper; use sp_core::U256; use sp_runtime::traits::{Dispatchable, UniqueSaturatedInto}; use sp_std::vec; -use crate::{Runtime, RuntimeCall}; - use crate::precompiles::{bytes_to_account_id, get_method_id, get_slice}; +use crate::{Runtime, RuntimeCall}; pub const BALANCE_TRANSFER_INDEX: u64 = 2048; +// This is a hardcoded hashed address mapping of 0x0000000000000000000000000000000000000800 to an +// ss58 public key i.e., the contract sends funds it received to the destination address from the +// method parameter. +const CONTRACT_ADDRESS_SS58: [u8; 32] = [ + 0x07, 0xec, 0x71, 0x2a, 0x5d, 0x38, 0x43, 0x4d, 0xdd, 0x03, 0x3f, 0x8f, 0x02, 0x4e, 0xcd, 0xfc, + 0x4b, 0xb5, 0x95, 0x1c, 0x13, 0xc3, 0x08, 0x5c, 0x39, 0x9c, 0x8a, 0x5f, 0x62, 0x93, 0x70, 0x5d, +]; + pub struct BalanceTransferPrecompile; impl BalanceTransferPrecompile { @@ -20,54 +28,50 @@ impl BalanceTransferPrecompile { let txdata = handle.input(); // Match method ID: keccak256("transfer(bytes32)") - let method: &[u8] = get_slice(txdata, 0, 4)?; - if get_method_id("transfer(bytes32)") == method { - // Forward all received value to the destination address - let amount: U256 = handle.context().apparent_value; + let method = get_slice(txdata, 0, 4)?; + if get_method_id("transfer(bytes32)") != method { + return Ok(PrecompileOutput { + exit_status: ExitSucceed::Returned, + output: vec![], + }); + } - // Use BalanceConverter to convert EVM amount to Substrate balance - let amount_sub = - <Runtime as pallet_evm::Config>::BalanceConverter::into_substrate_balance(amount) - .ok_or(ExitError::OutOfFund)?; + // Forward all received value to the destination address + let amount: U256 = handle.context().apparent_value; - if amount_sub.is_zero() { - return Ok(PrecompileOutput { - exit_status: ExitSucceed::Returned, - output: vec![], - }); - } + // Use BalanceConverter to convert EVM amount to Substrate balance + let amount_sub = + <Runtime as pallet_evm::Config>::BalanceConverter::into_substrate_balance(amount) + .ok_or(ExitError::OutOfFund)?; - // This is a hardcoded hashed address mapping of - // 0x0000000000000000000000000000000000000800 to an ss58 public key - // i.e., the contract sends funds it received to the destination address - // from the method parameter. - const ADDRESS_BYTES_SRC: [u8; 32] = [ - 0x07, 0xec, 0x71, 0x2a, 0x5d, 0x38, 0x43, 0x4d, 0xdd, 0x03, 0x3f, 0x8f, 0x02, 0x4e, - 0xcd, 0xfc, 0x4b, 0xb5, 0x95, 0x1c, 0x13, 0xc3, 0x08, 0x5c, 0x39, 0x9c, 0x8a, 0x5f, - 0x62, 0x93, 0x70, 0x5d, - ]; - let address_bytes_dst: &[u8] = get_slice(txdata, 4, 36)?; - let account_id_src = bytes_to_account_id(&ADDRESS_BYTES_SRC)?; - let account_id_dst = bytes_to_account_id(address_bytes_dst)?; + if amount_sub.is_zero() { + return Ok(PrecompileOutput { + exit_status: ExitSucceed::Returned, + output: vec![], + }); + } - let call = - RuntimeCall::Balances(pallet_balances::Call::<Runtime>::transfer_allow_death { - dest: account_id_dst.into(), - value: amount_sub.unique_saturated_into(), - }); + let address_bytes_dst = get_slice(txdata, 4, 36)?; + let account_id_src = bytes_to_account_id(&CONTRACT_ADDRESS_SS58)?; + let account_id_dst = bytes_to_account_id(address_bytes_dst)?; - // Dispatch the call - let result = call.dispatch(RawOrigin::Signed(account_id_src).into()); - if result.is_err() { - return Err(PrecompileFailure::Error { - exit_status: ExitError::OutOfFund, - }); - } - } + let call = RuntimeCall::Balances(pallet_balances::Call::<Runtime>::transfer_allow_death { + dest: account_id_dst.into(), + value: amount_sub.unique_saturated_into(), + }); - Ok(PrecompileOutput { + // Dispatch the call + RuntimeHelper::<Runtime>::try_dispatch( + handle, + RawOrigin::Signed(account_id_src).into(), + call, + ) + .map(|_| PrecompileOutput { exit_status: ExitSucceed::Returned, output: vec![], }) + .map_err(|_| PrecompileFailure::Error { + exit_status: ExitError::OutOfFund, + }) } } diff --git a/runtime/src/precompiles/staking.rs b/runtime/src/precompiles/staking.rs index 86d257a19..e670e3b3b 100644 --- a/runtime/src/precompiles/staking.rs +++ b/runtime/src/precompiles/staking.rs @@ -25,24 +25,24 @@ // - Precompile checks the result of do_remove_stake and, in case of a failure, reverts the transaction. // +use frame_support::dispatch::{GetDispatchInfo, Pays}; use frame_system::RawOrigin; -use pallet_evm::{AddressMapping, BalanceConverter, HashedAddressMapping}; use pallet_evm::{ - ExitError, ExitSucceed, PrecompileFailure, PrecompileHandle, PrecompileOutput, PrecompileResult, + AddressMapping, BalanceConverter, ExitError, ExitSucceed, GasWeightMapping, + HashedAddressMapping, PrecompileFailure, PrecompileHandle, PrecompileOutput, PrecompileResult, }; +use precompile_utils::prelude::RuntimeHelper; use sp_core::crypto::Ss58Codec; use sp_core::U256; -use sp_runtime::traits::Dispatchable; -use sp_runtime::traits::{BlakeTwo256, StaticLookup, UniqueSaturatedInto}; +use sp_runtime::traits::{BlakeTwo256, Dispatchable, StaticLookup, UniqueSaturatedInto}; use sp_runtime::AccountId32; +use sp_std::vec; use crate::{ precompiles::{get_method_id, get_slice}, - ProxyType, + ProxyType, Runtime, RuntimeCall, }; -use sp_std::vec; -use crate::{Runtime, RuntimeCall}; pub const STAKING_PRECOMPILE_INDEX: u64 = 2049; pub struct StakingPrecompile; @@ -209,24 +209,28 @@ impl StakingPrecompile { ); // Transfer the amount back to the caller before executing the staking operation - // let caller = handle.context().caller; let amount = handle.context().apparent_value; if !amount.is_zero() { Self::transfer_back_to_caller(&account_id, amount)?; } - let result = call.dispatch(RawOrigin::Signed(account_id.clone()).into()); - match &result { - Ok(post_info) => log::info!("Dispatch succeeded. Post info: {:?}", post_info), - Err(dispatch_error) => log::error!("Dispatch failed. Error: {:?}", dispatch_error), - } - match result { - Ok(_) => Ok(PrecompileOutput { - exit_status: ExitSucceed::Returned, - output: vec![], - }), - Err(_) => { + match RuntimeHelper::<Runtime>::try_dispatch( + handle, + RawOrigin::Signed(account_id.clone()).into(), + call, + ) { + Ok(post_info) => { + log::info!("Dispatch succeeded. Post info: {:?}", post_info); + + Ok(PrecompileOutput { + exit_status: ExitSucceed::Returned, + output: vec![], + }) + } + + Err(dispatch_error) => { + log::error!("Dispatch failed. Error: {:?}", dispatch_error); log::warn!("Returning error PrecompileFailure::Error"); Err(PrecompileFailure::Error { exit_status: ExitError::Other("Subtensor call failed".into()), From 900cccfbaee7aa854efc17ef2e00c4991fadecde Mon Sep 17 00:00:00 2001 From: camfairchild <cameron@opentensor.dev> Date: Thu, 23 Jan 2025 10:16:29 -0500 Subject: [PATCH 045/145] pay vali take in root-divs --- .../subtensor/src/coinbase/run_coinbase.rs | 141 +++++++++++++----- 1 file changed, 101 insertions(+), 40 deletions(-) diff --git a/pallets/subtensor/src/coinbase/run_coinbase.rs b/pallets/subtensor/src/coinbase/run_coinbase.rs index a8af8322c..9596c2bc3 100644 --- a/pallets/subtensor/src/coinbase/run_coinbase.rs +++ b/pallets/subtensor/src/coinbase/run_coinbase.rs @@ -356,11 +356,11 @@ impl<T: Config> Pallet<T> { } // Initialize maps for dividend calculations. - let mut root_divs: BTreeMap<T::AccountId, u64> = BTreeMap::new(); + let mut root_alpha_divs: BTreeMap<T::AccountId, u64> = BTreeMap::new(); let mut alpha_divs: BTreeMap<T::AccountId, u64> = BTreeMap::new(); - let mut validator_takes: BTreeMap<T::AccountId, u64> = BTreeMap::new(); - - // 2.2 --- Calculate the validator_take, alpha_divs, and root_divs using above dividend tuples. + let mut validator_alpha_takes: BTreeMap<T::AccountId, u64> = BTreeMap::new(); + let mut validator_root_alpha_takes: BTreeMap<T::AccountId, u64> = BTreeMap::new(); + // 2.2 --- Calculate the validator_take, alpha_divs, and root_alpha_divs using above dividend tuples. for (hotkey, dividend_tuples) in dividends_to_distribute.iter() { // Calculate the proportion of root vs alpha divs for each hotkey using their stake. for (hotkey_j, divs_j) in dividend_tuples.iter() { @@ -394,52 +394,67 @@ impl<T: Config> Pallet<T> { root_prop ); - // 2.3.1 --- Remove the hotkey take from both alpha and root divs. + let divs_j: I96F32 = I96F32::from_num(*divs_j); + // 2.3.1 --- Compute root dividends + let root_alpha_divs_j: I96F32 = divs_j.saturating_mul(root_prop); + // 2.3.2 --- Compute alpha dividends + let alpha_divs_j: I96F32 = divs_j.saturating_sub(root_alpha_divs_j); + log::debug!( + "Alpha dividends: {:?}, Root alpha-dividends: {:?}", + alpha_divs_j, + root_alpha_divs_j + ); + + // 2.4.1 --- Remove the hotkey take from both alpha and root divs. let take_prop: I96F32 = I96F32::from_num(Self::get_hotkey_take(hotkey_j)) .checked_div(I96F32::from_num(u16::MAX)) .unwrap_or(I96F32::from_num(0.0)); - let validator_take: I96F32 = take_prop.saturating_mul(I96F32::from_num(*divs_j)); - let rem_divs_j: I96F32 = I96F32::from_num(*divs_j).saturating_sub(validator_take); + + let validator_alpha_take: I96F32 = take_prop.saturating_mul(alpha_divs_j); + let validator_root_alpha_take: I96F32 = take_prop.saturating_mul(root_alpha_divs_j); + + let rem_alpha_divs_j: I96F32 = alpha_divs_j.saturating_sub(validator_alpha_take); + let rem_root_alpha_divs_j: I96F32 = + root_alpha_divs_j.saturating_sub(validator_root_alpha_take); log::debug!( - "Validator take for hotkey {:?}: {:?}, remaining dividends: {:?}", + "Validator take for hotkey {:?}: alpha take: {:?}, remaining alpha: {:?}, root-alpha take: {:?}, remaining root-alpha: {:?}", hotkey_j, - validator_take, - rem_divs_j + validator_alpha_take, + rem_alpha_divs_j, + validator_root_alpha_take, + rem_root_alpha_divs_j ); - // 2.3.2 --- Store the validator take. - validator_takes + // 2.4.2 --- Store the validator takes. + validator_alpha_takes + .entry(hotkey_j.clone()) + .and_modify(|e| *e = e.saturating_add(validator_alpha_take.to_num::<u64>())) + .or_insert(validator_alpha_take.to_num::<u64>()); + validator_root_alpha_takes .entry(hotkey_j.clone()) - .and_modify(|e| *e = e.saturating_add(validator_take.to_num::<u64>())) - .or_insert(validator_take.to_num::<u64>()); + .and_modify(|e| { + *e = e.saturating_add(validator_root_alpha_take.to_num::<u64>()) + }) + .or_insert(validator_root_alpha_take.to_num::<u64>()); log::debug!( - "Stored validator take for hotkey {:?}: {:?}", + "Stored validator take for hotkey {:?}: alpha take: {:?}, root-alpha take: {:?}", hotkey_j, - validator_take.to_num::<u64>() + validator_alpha_take.to_num::<u64>(), + validator_root_alpha_take.to_num::<u64>() ); - // 2.4.1 --- Compute root dividends - let root_divs_j: I96F32 = rem_divs_j.saturating_mul(root_prop); - // 2.4.2 --- Compute alpha dividends - let alpha_divs_j: I96F32 = rem_divs_j.saturating_sub(root_divs_j); - log::debug!( - "Alpha dividends: {:?}, root dividends: {:?}", - alpha_divs_j, - root_divs_j - ); - - // 2.4.3 --- Store the root divs under hotkey_j - root_divs + // 2.5.1 --- Store the root divs under hotkey_j + root_alpha_divs .entry(hotkey_j.clone()) - .and_modify(|e| *e = e.saturating_add(root_divs_j.to_num::<u64>())) - .or_insert(root_divs_j.to_num::<u64>()); + .and_modify(|e| *e = e.saturating_add(root_alpha_divs_j.to_num::<u64>())) + .or_insert(root_alpha_divs_j.to_num::<u64>()); log::debug!( "Stored root alpha dividends for hotkey {:?}: {:?}", hotkey_j, - root_divs_j.to_num::<u64>() + root_alpha_divs_j.to_num::<u64>() ); - // 2.4.4 --- Store the alpha dividends + // 2.5.2 --- Store the alpha dividends alpha_divs .entry(hotkey_j.clone()) .and_modify(|e| *e = e.saturating_add(alpha_divs_j.to_num::<u64>())) @@ -452,7 +467,10 @@ impl<T: Config> Pallet<T> { } } - let total_root_divs: u64 = root_divs.values().sum(); + let total_root_alpha_divs: u64 = root_alpha_divs + .values() + .sum() + .saturating_add(validator_root_alpha_takes.values().sum()); // === 3. Distribute the dividends to the hotkeys. @@ -488,9 +506,9 @@ impl<T: Config> Pallet<T> { ); } - // 3.3 --- Distribute validator take - for (validator_j, validator_take) in validator_takes { - // 3.3.1 --- Distribute validator take to the validator. + // 3.3.1 --- Distribute validator alpha takes + for (validator_j, validator_take) in validator_alpha_takes { + // 3.3.1a --- Distribute validator take to the validator. Self::increase_stake_for_hotkey_and_coldkey_on_subnet( &validator_j.clone(), &Owner::<T>::get(validator_j.clone()), @@ -504,7 +522,7 @@ impl<T: Config> Pallet<T> { validator_take ); - // 3.3.2 --- Record dividends for this validator on this subnet. + // 3.3.1b --- Record dividends for this validator on this subnet. AlphaDividendsPerSubnet::<T>::mutate(netuid, validator_j.clone(), |divs| { *divs = divs.saturating_add(validator_take); }); @@ -516,6 +534,49 @@ impl<T: Config> Pallet<T> { ); } + // 3.3.2 --- Distribute validator root-alpha takes + for (validator_j, validator_take) in validator_root_alpha_takes { + // 3.3.2a --- Calculate the proportion of root divs to pay out to this validator's take. + let proportion: I96F32 = I96F32::from_num(validator_take) + .checked_div(I96F32::from_num(total_root_alpha_divs)) + .unwrap_or(I96F32::from_num(0)); + // 3.3.2b --- Get the proportion of root divs from the pending root divs. + let take_as_root_divs: u64 = proportion + .saturating_mul(I96F32::from_num(pending_root_divs)) + .to_num::<u64>(); + log::debug!( + "Root div proportion for validator take {:?}: {:?}, take_as_root_divs: {:?}", + validator_take, + proportion, + take_as_root_divs + ); + + // 3.3.2c --- Distribute validator take to the validator. + Self::increase_stake_for_hotkey_and_coldkey_on_subnet( + &validator_j.clone(), + &Owner::<T>::get(validator_j.clone()), + Self::get_root_netuid(), + take_as_root_divs, + ); + log::debug!( + "Distributed validator take for hotkey {:?} on root netuid {:?}: {:?}", + validator_j, + Self::get_root_netuid(), + take_as_root_divs + ); + + // 3.3.2d --- Record dividends for this validator on this subnet. + TaoDividendsPerSubnet::<T>::mutate(netuid, validator_j.clone(), |divs| { + *divs = divs.saturating_add(take_as_root_divs); + }); + log::debug!( + "Recorded dividends for validator {:?} on netuid {:?}: {:?}", + validator_j, + netuid, + take_as_root_divs + ); + } + // 3.4 --- Distribute alpha divs for (hotkey_j, alpha_divs_j) in alpha_divs { // 3.4.1 --- Distribute alpha divs to the hotkey. @@ -541,10 +602,10 @@ impl<T: Config> Pallet<T> { // 3.5 --- Distribute root divs // For all the root-alpha divs give this proportion of the swapped tao to the root participants. - for (hotkey_j, root_divs_j) in root_divs.iter() { + for (hotkey_j, root_alpha_divs_j) in root_alpha_divs.iter() { // 3.5.1 --- Calculate the proportion of root divs to pay out to this hotkey. - let proportion: I96F32 = I96F32::from_num(*root_divs_j) - .checked_div(I96F32::from_num(total_root_divs)) + let proportion: I96F32 = I96F32::from_num(*root_alpha_divs_j) + .checked_div(I96F32::from_num(total_root_alpha_divs)) .unwrap_or(I96F32::from_num(0)); // 3.5.2 --- Get the proportion of root divs from the pending root divs. let root_divs_to_pay: u64 = proportion From fe34f5aa7bc8517eab83f5715717566b0a035498 Mon Sep 17 00:00:00 2001 From: camfairchild <cameron@opentensor.dev> Date: Thu, 23 Jan 2025 10:17:43 -0500 Subject: [PATCH 046/145] specify sum-type --- pallets/subtensor/src/coinbase/run_coinbase.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pallets/subtensor/src/coinbase/run_coinbase.rs b/pallets/subtensor/src/coinbase/run_coinbase.rs index 9596c2bc3..d340271bb 100644 --- a/pallets/subtensor/src/coinbase/run_coinbase.rs +++ b/pallets/subtensor/src/coinbase/run_coinbase.rs @@ -469,8 +469,8 @@ impl<T: Config> Pallet<T> { let total_root_alpha_divs: u64 = root_alpha_divs .values() - .sum() - .saturating_add(validator_root_alpha_takes.values().sum()); + .sum::<u64>() + .saturating_add(validator_root_alpha_takes.values().sum::<u64>()); // === 3. Distribute the dividends to the hotkeys. From d66e2ed8197bf37120943ab023379b1b7df93d5f Mon Sep 17 00:00:00 2001 From: Aliaksandr Tsurko <ales@opentensor.dev> Date: Thu, 23 Jan 2025 16:18:19 +0100 Subject: [PATCH 047/145] Reformat --- node/src/chain_spec/localnet.rs | 4 ++-- runtime/src/precompiles/balance_transfer.rs | 2 +- runtime/src/precompiles/staking.rs | 5 ++--- 3 files changed, 5 insertions(+), 6 deletions(-) diff --git a/node/src/chain_spec/localnet.rs b/node/src/chain_spec/localnet.rs index 4aa84f8ca..3f0743199 100644 --- a/node/src/chain_spec/localnet.rs +++ b/node/src/chain_spec/localnet.rs @@ -77,7 +77,7 @@ fn localnet_genesis( get_account_id_from_seed::<sr25519::Public>("Ferdie"), 2000000000000u128, ), - // ETH + // ETH ( // Alith - 0xf24FF3a9CF04c71Dbc94D0b566f7A27B94566cac AccountId::from_str("5Fghzk1AJt88PeFEzuRfXzbPchiBbsVGTTXcdx599VdZzkTA").unwrap(), @@ -131,7 +131,7 @@ fn localnet_genesis( "senateMembers": { "members": senate_members, }, - "evmChainId": { + "evmChainId": { "chainId": 42, }, }) diff --git a/runtime/src/precompiles/balance_transfer.rs b/runtime/src/precompiles/balance_transfer.rs index 7c534212d..03c4be8a6 100644 --- a/runtime/src/precompiles/balance_transfer.rs +++ b/runtime/src/precompiles/balance_transfer.rs @@ -5,7 +5,7 @@ use pallet_evm::{ }; use precompile_utils::prelude::RuntimeHelper; use sp_core::U256; -use sp_runtime::traits::{Dispatchable, UniqueSaturatedInto}; +use sp_runtime::traits::UniqueSaturatedInto; use sp_std::vec; use crate::precompiles::{bytes_to_account_id, get_method_id, get_slice}; diff --git a/runtime/src/precompiles/staking.rs b/runtime/src/precompiles/staking.rs index e670e3b3b..f8534927b 100644 --- a/runtime/src/precompiles/staking.rs +++ b/runtime/src/precompiles/staking.rs @@ -25,11 +25,10 @@ // - Precompile checks the result of do_remove_stake and, in case of a failure, reverts the transaction. // -use frame_support::dispatch::{GetDispatchInfo, Pays}; use frame_system::RawOrigin; use pallet_evm::{ - AddressMapping, BalanceConverter, ExitError, ExitSucceed, GasWeightMapping, - HashedAddressMapping, PrecompileFailure, PrecompileHandle, PrecompileOutput, PrecompileResult, + AddressMapping, BalanceConverter, ExitError, ExitSucceed, HashedAddressMapping, + PrecompileFailure, PrecompileHandle, PrecompileOutput, PrecompileResult, }; use precompile_utils::prelude::RuntimeHelper; use sp_core::crypto::Ss58Codec; From 541518e091b2fe4150293a740f54f437fc47006a Mon Sep 17 00:00:00 2001 From: open-junius <zhou@opentensor.dev> Date: Thu, 23 Jan 2025 23:28:03 +0800 Subject: [PATCH 048/145] fix compilation error --- runtime/src/precompiles/mod.rs | 4 ++-- runtime/src/precompiles/subnet.rs | 5 +++-- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/runtime/src/precompiles/mod.rs b/runtime/src/precompiles/mod.rs index 3c62d4b8c..de4f4fc1f 100644 --- a/runtime/src/precompiles/mod.rs +++ b/runtime/src/precompiles/mod.rs @@ -233,9 +233,9 @@ fn dispatch( } } -pub fn get_pubkey(data: &[u8]) -> Result<(AccountId32, Vec<u8>), PrecompileFailure> { +pub fn get_pubkey(data: &[u8]) -> Result<(AccountId32, vec::Vec<u8>), PrecompileFailure> { let mut pubkey = [0u8; 32]; - pubkey.copy_from_slice(data.get_slice(0, 32)?); + pubkey.copy_from_slice(get_slice(data, 0, 32)?); Ok(( pubkey.into(), diff --git a/runtime/src/precompiles/subnet.rs b/runtime/src/precompiles/subnet.rs index 9c9fd859f..7537ea8ae 100644 --- a/runtime/src/precompiles/subnet.rs +++ b/runtime/src/precompiles/subnet.rs @@ -1,6 +1,7 @@ use crate::precompiles::{dispatch, get_method_id, get_pubkey, get_slice}; use crate::{Runtime, RuntimeCall}; use pallet_evm::{ExitError, PrecompileFailure, PrecompileHandle, PrecompileResult}; +use sp_runtime::AccountId32; use sp_std::vec; pub const SUBNET_PRECOMPILE_INDEX: u64 = 2051; @@ -42,7 +43,7 @@ impl SubnetPrecompile { fn register_network(handle: &mut impl PrecompileHandle, data: &[u8]) -> PrecompileResult { let call = match data.len() { 32 => { - let (hotkey, _) = get_pubkey(data); + let (hotkey, _) = get_pubkey(data)?; RuntimeCall::SubtensorModule( pallet_subtensor::Call::<Runtime>::register_network_with_identity { @@ -83,7 +84,7 @@ impl SubnetPrecompile { fn parse_register_network_parameters( data: &[u8], - ) -> Result<([u8; 32], vec::Vec<u8>, vec::Vec<u8>, vec::Vec<u8>), PrecompileFailure> { + ) -> Result<(AccountId32, vec::Vec<u8>, vec::Vec<u8>, vec::Vec<u8>), PrecompileFailure> { let (pubkey, _) = get_pubkey(data)?; let mut buf = [0_u8; 4]; From d148c2b3a357ad5229edbed46da06517624fe68c Mon Sep 17 00:00:00 2001 From: open-junius <zhou@opentensor.dev> Date: Thu, 23 Jan 2025 23:44:21 +0800 Subject: [PATCH 049/145] refactor get pubkey method --- runtime/src/precompiles/neuron.rs | 12 +++---- runtime/src/precompiles/solidity/subnet.sol | 2 +- runtime/src/precompiles/staking.rs | 38 ++++----------------- runtime/src/precompiles/subnet.rs | 6 ++-- 4 files changed, 17 insertions(+), 41 deletions(-) diff --git a/runtime/src/precompiles/neuron.rs b/runtime/src/precompiles/neuron.rs index 341683cb3..efa0b5fec 100644 --- a/runtime/src/precompiles/neuron.rs +++ b/runtime/src/precompiles/neuron.rs @@ -1,6 +1,7 @@ use pallet_evm::{ExitError, PrecompileFailure, PrecompileHandle, PrecompileResult}; -use crate::precompiles::{dispatch, get_method_id, get_slice}; +use crate::precompiles::{dispatch, get_method_id, get_pubkey, get_slice}; +use sp_runtime::AccountId32; use sp_std::vec; use crate::{Runtime, RuntimeCall}; @@ -35,12 +36,12 @@ impl NeuronPrecompile { let call = RuntimeCall::SubtensorModule(pallet_subtensor::Call::<Runtime>::burned_register { netuid, - hotkey: hotkey.into(), + hotkey, }); dispatch(handle, call, NEURON_CONTRACT_ADDRESS) } - fn parse_netuid_hotkey_parameter(data: &[u8]) -> Result<(u16, [u8; 32]), PrecompileFailure> { + fn parse_netuid_hotkey_parameter(data: &[u8]) -> Result<(u16, AccountId32), PrecompileFailure> { if data.len() < 64 { return Err(PrecompileFailure::Error { exit_status: ExitError::InvalidRange, @@ -50,9 +51,8 @@ impl NeuronPrecompile { netuid_vec.copy_from_slice(get_slice(data, 30, 32)?); let netuid = u16::from_be_bytes(netuid_vec); - let mut parameter = [0u8; 32]; - parameter.copy_from_slice(get_slice(data, 32, 64)?); + let (hotkey, _) = get_pubkey(get_slice(data, 32, 64)?)?; - Ok((netuid, parameter)) + Ok((netuid, hotkey)) } } diff --git a/runtime/src/precompiles/solidity/subnet.sol b/runtime/src/precompiles/solidity/subnet.sol index c3d0a4a13..c6639891a 100644 --- a/runtime/src/precompiles/solidity/subnet.sol +++ b/runtime/src/precompiles/solidity/subnet.sol @@ -1,6 +1,6 @@ pragma solidity ^0.8.0; -address constant ISTAKING_ADDRESS = 0x0000000000000000000000000000000000000803; +address constant ISUBNET_ADDRESS = 0x0000000000000000000000000000000000000803; interface ISubnet { /// Registers a new network without specifying details. diff --git a/runtime/src/precompiles/staking.rs b/runtime/src/precompiles/staking.rs index a29031352..f549ee1c3 100644 --- a/runtime/src/precompiles/staking.rs +++ b/runtime/src/precompiles/staking.rs @@ -31,10 +31,9 @@ use pallet_evm::{ }; use sp_core::U256; use sp_runtime::traits::{StaticLookup, UniqueSaturatedInto}; -use sp_runtime::AccountId32; use crate::{ - precompiles::{dispatch, get_method_id, get_slice}, + precompiles::{dispatch, get_method_id, get_pubkey, get_slice}, ProxyType, }; use sp_std::vec; @@ -71,7 +70,7 @@ impl StakingPrecompile { } fn add_stake(handle: &mut impl PrecompileHandle, data: &[u8]) -> PrecompileResult { - let hotkey = Self::parse_pub_key(data)?.into(); + let (hotkey, _) = get_pubkey(data)?; let amount: U256 = handle.context().apparent_value; let netuid = Self::parse_netuid(data, 0x3E)?; @@ -90,7 +89,7 @@ impl StakingPrecompile { } fn remove_stake(handle: &mut impl PrecompileHandle, data: &[u8]) -> PrecompileResult { - let hotkey = Self::parse_pub_key(data)?.into(); + let (hotkey, _) = get_pubkey(data)?; let netuid = Self::parse_netuid(data, 0x5E)?; // We have to treat this as uint256 (because of Solidity ABI encoding rules, it pads uint64), @@ -113,7 +112,7 @@ impl StakingPrecompile { } fn add_proxy(handle: &mut impl PrecompileHandle, data: &[u8]) -> PrecompileResult { - let delegate = AccountId32::from(Self::parse_pub_key(data)?); + let (delegate, _) = get_pubkey(data)?; let delegate = <Runtime as frame_system::Config>::Lookup::unlookup(delegate); let call = RuntimeCall::Proxy(pallet_proxy::Call::<Runtime>::add_proxy { delegate, @@ -125,7 +124,7 @@ impl StakingPrecompile { } fn remove_proxy(handle: &mut impl PrecompileHandle, data: &[u8]) -> PrecompileResult { - let delegate = AccountId32::from(Self::parse_pub_key(data)?); + let (delegate, _) = get_pubkey(data)?; let delegate = <Runtime as frame_system::Config>::Lookup::unlookup(delegate); let call = RuntimeCall::Proxy(pallet_proxy::Call::<Runtime>::remove_proxy { delegate, @@ -137,7 +136,8 @@ impl StakingPrecompile { } fn get_stake(data: &[u8]) -> PrecompileResult { - let (hotkey, coldkey) = Self::parse_hotkey_coldkey(data)?; + let (hotkey, left_data) = get_pubkey(data)?; + let (coldkey, _) = get_pubkey(&left_data)?; let netuid = Self::parse_netuid(data, 0x5E)?; let stake = pallet_subtensor::Pallet::<Runtime>::get_stake_for_hotkey_and_coldkey_on_subnet( @@ -162,30 +162,6 @@ impl StakingPrecompile { }) } - fn parse_hotkey_coldkey(data: &[u8]) -> Result<([u8; 32], [u8; 32]), PrecompileFailure> { - if data.len() < 64 { - return Err(PrecompileFailure::Error { - exit_status: ExitError::InvalidRange, - }); - } - let mut hotkey = [0u8; 32]; - hotkey.copy_from_slice(get_slice(data, 0, 32)?); - let mut coldkey = [0u8; 32]; - coldkey.copy_from_slice(get_slice(data, 32, 64)?); - Ok((hotkey, coldkey)) - } - - fn parse_pub_key(data: &[u8]) -> Result<[u8; 32], PrecompileFailure> { - if data.len() < 32 { - return Err(PrecompileFailure::Error { - exit_status: ExitError::InvalidRange, - }); - } - let mut pubkey = [0u8; 32]; - pubkey.copy_from_slice(get_slice(data, 0, 32)?); - Ok(pubkey) - } - fn parse_netuid(data: &[u8], offset: usize) -> Result<u16, PrecompileFailure> { if data.len() < offset + 2 { return Err(PrecompileFailure::Error { diff --git a/runtime/src/precompiles/subnet.rs b/runtime/src/precompiles/subnet.rs index 7537ea8ae..3dba5a262 100644 --- a/runtime/src/precompiles/subnet.rs +++ b/runtime/src/precompiles/subnet.rs @@ -47,13 +47,13 @@ impl SubnetPrecompile { RuntimeCall::SubtensorModule( pallet_subtensor::Call::<Runtime>::register_network_with_identity { - hotkey: hotkey.into(), + hotkey, identity: None, }, ) } 33.. => { - let (pubkey, subnet_name, github_repo, subnet_contact) = + let (hotkey, subnet_name, github_repo, subnet_contact) = Self::parse_register_network_parameters(data)?; let identity: pallet_subtensor::SubnetIdentityOf = @@ -66,7 +66,7 @@ impl SubnetPrecompile { // Create the register_network callcle RuntimeCall::SubtensorModule( pallet_subtensor::Call::<Runtime>::register_network_with_identity { - hotkey: pubkey.into(), + hotkey, identity: Some(identity), }, ) From 96a1d08dd15581752480661122e25b1207ba8979 Mon Sep 17 00:00:00 2001 From: open-junius <zhou@opentensor.dev> Date: Thu, 23 Jan 2025 23:55:31 +0800 Subject: [PATCH 050/145] cargo clippy --- 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 f549ee1c3..f6e358758 100644 --- a/runtime/src/precompiles/staking.rs +++ b/runtime/src/precompiles/staking.rs @@ -141,8 +141,8 @@ impl StakingPrecompile { let netuid = Self::parse_netuid(data, 0x5E)?; let stake = pallet_subtensor::Pallet::<Runtime>::get_stake_for_hotkey_and_coldkey_on_subnet( - &hotkey.into(), - &coldkey.into(), + &hotkey, + &coldkey, netuid, ); From 241d6204893936fad235cb2d247f5972bad2f1da Mon Sep 17 00:00:00 2001 From: open-junius <zhou@opentensor.dev> Date: Thu, 23 Jan 2025 23:55:57 +0800 Subject: [PATCH 051/145] cargo fmt --- runtime/src/precompiles/staking.rs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/runtime/src/precompiles/staking.rs b/runtime/src/precompiles/staking.rs index f6e358758..f9a0968f4 100644 --- a/runtime/src/precompiles/staking.rs +++ b/runtime/src/precompiles/staking.rs @@ -141,9 +141,7 @@ impl StakingPrecompile { let netuid = Self::parse_netuid(data, 0x5E)?; let stake = pallet_subtensor::Pallet::<Runtime>::get_stake_for_hotkey_and_coldkey_on_subnet( - &hotkey, - &coldkey, - netuid, + &hotkey, &coldkey, netuid, ); // Convert to EVM decimals From 65aad3c04ad64a7ca6eeeb517a81e863c731b534 Mon Sep 17 00:00:00 2001 From: camfairchild <cameron@opentensor.dev> Date: Thu, 23 Jan 2025 11:05:20 -0500 Subject: [PATCH 052/145] add fee for swap stake --- pallets/subtensor/src/staking/move_stake.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/pallets/subtensor/src/staking/move_stake.rs b/pallets/subtensor/src/staking/move_stake.rs index 38a783b25..fff688084 100644 --- a/pallets/subtensor/src/staking/move_stake.rs +++ b/pallets/subtensor/src/staking/move_stake.rs @@ -288,8 +288,9 @@ impl<T: Config> Pallet<T> { ); // 6. Unstake from the origin subnet, returning TAO (or a 1:1 equivalent). + let fee = DefaultMinStake::<T>::get().saturating_div(2); let tao_unstaked = - Self::unstake_from_subnet(&hotkey, &coldkey, origin_netuid, alpha_amount); + Self::unstake_from_subnet(&hotkey, &coldkey, origin_netuid, alpha_amount, fee); // 7. Check that the unstaked amount is above the minimum stake threshold. ensure!( @@ -298,7 +299,7 @@ impl<T: Config> Pallet<T> { ); // 8. Stake the unstaked amount into the destination subnet, using the same coldkey/hotkey. - Self::stake_into_subnet(&hotkey, &coldkey, destination_netuid, tao_unstaked); + Self::stake_into_subnet(&hotkey, &coldkey, destination_netuid, tao_unstaked, fee); // 9. Emit an event for logging. log::info!( From 438b67837047f5fe2ecadc100e54497c59b58523 Mon Sep 17 00:00:00 2001 From: camfairchild <cameron@opentensor.dev> Date: Thu, 23 Jan 2025 11:06:25 -0500 Subject: [PATCH 053/145] add 0-fee to tests --- pallets/subtensor/src/tests/move_stake.rs | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/pallets/subtensor/src/tests/move_stake.rs b/pallets/subtensor/src/tests/move_stake.rs index 150fa1201..04f79f3f9 100644 --- a/pallets/subtensor/src/tests/move_stake.rs +++ b/pallets/subtensor/src/tests/move_stake.rs @@ -1085,7 +1085,7 @@ fn test_do_swap_success() { let stake_amount = DefaultMinStake::<Test>::get() * 10; SubtensorModule::create_account_if_non_existent(&coldkey, &hotkey); - SubtensorModule::stake_into_subnet(&hotkey, &coldkey, origin_netuid, stake_amount); + SubtensorModule::stake_into_subnet(&hotkey, &coldkey, origin_netuid, stake_amount, 0); let alpha_before = SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( &hotkey, &coldkey, @@ -1180,7 +1180,7 @@ fn test_do_swap_insufficient_stake() { let attempted_swap = stake_amount * 2; SubtensorModule::create_account_if_non_existent(&coldkey, &hotkey); - SubtensorModule::stake_into_subnet(&hotkey, &coldkey, netuid1, stake_amount); + SubtensorModule::stake_into_subnet(&hotkey, &coldkey, netuid1, stake_amount, 0); assert_noop!( SubtensorModule::do_swap_stake( @@ -1209,7 +1209,7 @@ fn test_do_swap_wrong_origin() { let stake_amount = 100_000; SubtensorModule::create_account_if_non_existent(&real_coldkey, &hotkey); - SubtensorModule::stake_into_subnet(&hotkey, &real_coldkey, netuid1, stake_amount); + SubtensorModule::stake_into_subnet(&hotkey, &real_coldkey, netuid1, stake_amount, 0); assert_noop!( SubtensorModule::do_swap_stake( @@ -1237,7 +1237,7 @@ fn test_do_swap_minimum_stake_check() { let swap_amount = 1; SubtensorModule::create_account_if_non_existent(&coldkey, &hotkey); - SubtensorModule::stake_into_subnet(&hotkey, &coldkey, netuid, total_stake); + SubtensorModule::stake_into_subnet(&hotkey, &coldkey, netuid, total_stake, 0); assert_err!( SubtensorModule::do_swap_stake( @@ -1264,7 +1264,7 @@ fn test_do_swap_same_subnet() { let stake_amount = DefaultMinStake::<Test>::get() * 10; SubtensorModule::create_account_if_non_existent(&coldkey, &hotkey); - SubtensorModule::stake_into_subnet(&hotkey, &coldkey, netuid, stake_amount); + SubtensorModule::stake_into_subnet(&hotkey, &coldkey, netuid, stake_amount, 0); let alpha_before = SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet(&hotkey, &coldkey, netuid); @@ -1296,7 +1296,7 @@ fn test_do_swap_partial_stake() { let total_stake = DefaultMinStake::<Test>::get() * 10; SubtensorModule::create_account_if_non_existent(&coldkey, &hotkey); - SubtensorModule::stake_into_subnet(&hotkey, &coldkey, origin_netuid, total_stake); + SubtensorModule::stake_into_subnet(&hotkey, &coldkey, origin_netuid, total_stake, 0); let swap_amount = total_stake / 2; assert_ok!(SubtensorModule::do_swap_stake( @@ -1341,7 +1341,7 @@ fn test_do_swap_storage_updates() { let stake_amount = DefaultMinStake::<Test>::get() * 10; SubtensorModule::create_account_if_non_existent(&coldkey, &hotkey); - SubtensorModule::stake_into_subnet(&hotkey, &coldkey, origin_netuid, stake_amount); + SubtensorModule::stake_into_subnet(&hotkey, &coldkey, origin_netuid, stake_amount, 0); let alpha = SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( &hotkey, @@ -1390,7 +1390,7 @@ fn test_do_swap_multiple_times() { let initial_stake = DefaultMinStake::<Test>::get() * 10; SubtensorModule::create_account_if_non_existent(&coldkey, &hotkey); - SubtensorModule::stake_into_subnet(&hotkey, &coldkey, netuid1, initial_stake); + SubtensorModule::stake_into_subnet(&hotkey, &coldkey, netuid1, initial_stake, 0); for _ in 0..3 { let alpha1 = SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( From b42641f93ac9e8eed72067569153caf0648fa91a Mon Sep 17 00:00:00 2001 From: andreea-popescu-reef <160024917+andreea-popescu-reef@users.noreply.github.com> Date: Fri, 24 Jan 2025 00:22:55 +0800 Subject: [PATCH 054/145] Add bonds_penalty hyperparam (#1160) * add bonds_penalty hyperparam * fix safe math * 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/mock.rs | 2 + pallets/admin-utils/src/tests/mod.rs | 33 ++++ pallets/admin-utils/src/weights.rs | 27 +++ pallets/subtensor/src/epoch/math.rs | 84 ++++++++ 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 + 16 files changed, 604 insertions(+), 117 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::<T>::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::<T>::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(<T as Config>::WeightInfo::sudo_set_bonds_penalty())] + pub fn sudo_set_bonds_penalty( + origin: OriginFor<T>, + netuid: u16, + bonds_penalty: u16, + ) -> DispatchResult { + pallet_subtensor::Pallet::<T>::ensure_subnet_owner_or_root(origin, netuid)?; + + ensure!( + pallet_subtensor::Pallet::<T>::if_subnet_exist(netuid), + Error::<T>::SubnetDoesNotExist + ); + pallet_subtensor::Pallet::<T>::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/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..a3b771444 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; + add_network(netuid, 10); + let init_value: u16 = SubtensorModule::get_bonds_penalty(netuid); + assert_eq!( + AdminUtils::sudo_set_bonds_penalty( + <<Test as Config>::RuntimeOrigin>::signed(U256::from(1)), + netuid, + to_be_set + ), + Err(DispatchError::BadOrigin) + ); + assert_eq!( + AdminUtils::sudo_set_bonds_penalty( + <<Test as Config>::RuntimeOrigin>::root(), + netuid + 1, + to_be_set + ), + Err(Error::<Test>::SubnetDoesNotExist.into()) + ); + assert_eq!(SubtensorModule::get_bonds_penalty(netuid), init_value); + assert_ok!(AdminUtils::sudo_set_bonds_penalty( + <<Test as Config>::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/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<T: frame_system::Config> WeightInfo for SubstrateWeight<T> { } /// 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 57d0f6b6f..616a9b78b 100644 --- a/pallets/subtensor/src/epoch/math.rs +++ b/pallets/subtensor/src/epoch/math.rs @@ -1022,6 +1022,90 @@ pub fn weighted_median_col_sparse( median } +// Element-wise interpolation of two matrices: Result = A + ratio * (B - A). +// ratio has intended range [0, 1] +// ratio=0: Result = A +// ratio=1: Result = B +#[allow(dead_code)] +pub fn interpolate(mat1: &[Vec<I32F32>], mat2: &[Vec<I32F32>], ratio: I32F32) -> Vec<Vec<I32F32>> { + if ratio == I32F32::from_num(0) { + return mat1.to_owned(); + } + if ratio == I32F32::from_num(1) { + return mat2.to_owned(); + } + assert!(mat1.len() == mat2.len()); + if mat1.is_empty() { + return vec![vec![]; 1]; + } + if mat1.first().unwrap_or(&vec![]).is_empty() { + return vec![vec![]; 1]; + } + let mut result: Vec<Vec<I32F32>> = + 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 +} + +// Element-wise interpolation of two sparse matrices: Result = A + ratio * (B - A). +// ratio has intended range [0, 1] +// ratio=0: Result = A +// ratio=1: Result = B +#[allow(dead_code)] +pub fn interpolate_sparse( + mat1: &[Vec<(u16, I32F32)>], + mat2: &[Vec<(u16, I32F32)>], + columns: u16, + ratio: I32F32, +) -> Vec<Vec<(u16, I32F32)>> { + if ratio == I32F32::from_num(0) { + return mat1.to_owned(); + } + if ratio == I32F32::from_num(1) { + return mat2.to_owned(); + } + assert!(mat1.len() == mat2.len()); + let rows = mat1.len(); + let zero: I32F32 = I32F32::from_num(0); + let mut result: Vec<Vec<(u16, I32F32)>> = vec![vec![]; rows]; + for i in 0..rows { + let mut row1: Vec<I32F32> = vec![zero; columns as usize]; + 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<I32F32> = vec![zero; columns as usize]; + 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 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 { + if let Some(res) = result.get_mut(i) { + res.push((j as u16, interp)); + } + } + } + } + result +} + // Element-wise product of two matrices. #[allow(dead_code)] pub fn hadamard(mat1: &[Vec<I32F32>], mat2: &[Vec<I32F32>]) -> Vec<Vec<I32F32>> { 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<T: Config> Pallet<T> { // Compute preranks: r_j = SUM(i) w_ij * s_i let preranks: Vec<I32F32> = 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<I32F32> = weighted_median_col(&active_stake, &weights, kappa); - inplace_col_clip(&mut weights, &consensus); - let validator_trust: Vec<I32F32> = row_sum(&weights); + // Clip weights at majority consensus. + let mut clipped_weights: Vec<Vec<I32F32>> = weights.clone(); + inplace_col_clip(&mut clipped_weights, &consensus); + // Calculate validator trust as sum of clipped weights set by validator. + let validator_trust: Vec<I32F32> = row_sum(&clipped_weights); // ==================================== // == Ranks, Server Trust, Incentive == // ==================================== // Compute ranks: r_j = SUM(i) w_ij * s_i - let mut ranks: Vec<I32F32> = matmul(&weights, &active_stake); + let mut ranks: Vec<I32F32> = matmul(&clipped_weights, &active_stake); // Compute server trust: ratio of rank after vs. rank before. let trust: Vec<I32F32> = vecdiv(&ranks, &preranks); @@ -161,6 +165,14 @@ impl<T: Config> Pallet<T> { // == 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<Vec<I32F32>> = + interpolate(&weights, &clipped_weights, bonds_penalty); + // Access network bonds. let mut bonds: Vec<Vec<I32F32>> = Self::get_bonds(netuid); inplace_mask_matrix(&outdated, &mut bonds); // mask outdated bonds @@ -168,7 +180,7 @@ impl<T: Config> Pallet<T> { log::trace!("B:\n{:?}\n", &bonds); // Compute bonds delta column normalized. - let mut bonds_delta: Vec<Vec<I32F32>> = row_hadamard(&weights, &active_stake); // ΔB = W◦S + let mut bonds_delta: Vec<Vec<I32F32>> = 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<T: Config> Pallet<T> { let preranks: Vec<I32F32> = 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<I32F32> = 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<Vec<(u16, I32F32)>> = col_clip_sparse(&weights, &consensus); + log::trace!("Clipped Weights: {:?}", &clipped_weights); - let validator_trust: Vec<I32F32> = row_sum_sparse(&weights); + // Calculate validator trust as sum of clipped weights set by validator. + let validator_trust: Vec<I32F32> = row_sum_sparse(&clipped_weights); log::trace!("Validator Trust: {:?}", &validator_trust); // ============================= @@ -490,7 +505,7 @@ impl<T: Config> Pallet<T> { // ============================= // Compute ranks: r_j = SUM(i) w_ij * s_i. - let mut ranks: Vec<I32F32> = matmul_sparse(&weights, &active_stake, n); + let mut ranks: Vec<I32F32> = 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<T: Config> Pallet<T> { // == 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<Vec<(u16, I32F32)>> = + interpolate_sparse(&weights, &clipped_weights, n, bonds_penalty); + // Access network bonds. let mut bonds: Vec<Vec<(u16, I32F32)>> = Self::get_bonds_sparse(netuid); log::trace!("B: {:?}", &bonds); @@ -523,7 +546,8 @@ impl<T: Config> Pallet<T> { log::trace!("B (mask+norm): {:?}", &bonds); // Compute bonds delta column normalized. - let mut bonds_delta: Vec<Vec<(u16, I32F32)>> = row_hadamard_sparse(&weights, &active_stake); // ΔB = W◦S (outdated W masked) + let mut bonds_delta: Vec<Vec<(u16, I32F32)>> = + 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<T: Config> Pallet<T> { 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<u64> { let n = Self::get_subnetwork_n(netuid); diff --git a/pallets/subtensor/src/lib.rs b/pallets/subtensor/src/lib.rs index 6ecb69c87..9c4c5d0d4 100644 --- a/pallets/subtensor/src/lib.rs +++ b/pallets/subtensor/src/lib.rs @@ -572,6 +572,11 @@ pub mod pallet { pub fn DefaultBondsMovingAverage<T: Config>() -> u64 { T::InitialBondsMovingAverage::get() } + /// Default bonds penalty. + #[pallet::type_value] + pub fn DefaultBondsPenalty<T: Config>() -> u16 { + T::InitialBondsPenalty::get() + } #[pallet::type_value] /// Default validator prune length. pub fn DefaultValidatorPruneLen<T: Config>() -> u64 { @@ -1184,6 +1189,10 @@ pub mod pallet { /// --- MAP ( netuid ) --> bonds_moving_average pub type BondsMovingAverage<T> = StorageMap<_, Identity, u16, u64, ValueQuery, DefaultBondsMovingAverage<T>>; + #[pallet::storage] + /// --- MAP ( netuid ) --> bonds_penalty + pub type BondsPenalty<T> = + StorageMap<_, Identity, u16, u16, ValueQuery, DefaultBondsPenalty<T>>; /// --- MAP ( netuid ) --> weights_set_rate_limit #[pallet::storage] pub type WeightsSetRateLimit<T> = 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<u64>; + /// Initial bonds penalty. + #[pallet::constant] + type InitialBondsPenalty: Get<u16>; /// Initial target registrations per interval. #[pallet::constant] type InitialTargetRegistrationsPerInterval: Get<u16>; diff --git a/pallets/subtensor/src/macros/events.rs b/pallets/subtensor/src/macros/events.rs index 9c83b77c7..6dd6ddad2 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..7973a44e1 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<u16>, - Vec<u16>, - Vec<u16>, - Vec<u64>, - Vec<I32F32>, - Vec<I32F32>, - ) = (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 [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<u16>, + Vec<u16>, + Vec<u16>, + Vec<u64>, + Vec<I32F32>, + Vec<I32F32>, + ) = (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, + 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, + &[], + false, + &[], + 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::<u64>()).sum::<u64>(), 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..84fcc4aac 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<I32F32>> = vec![vec![]]; + let mat2: Vec<Vec<I32F32>> = vec![vec![]]; + let target: Vec<Vec<I32F32>> = 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<I32F32>> = vec![vec![I32F32::from_num(0)]]; + let mat2: Vec<Vec<I32F32>> = vec![vec![I32F32::from_num(1)]]; + let target: Vec<Vec<I32F32>> = 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<I32F32>> = 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<f32> = vec![0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.]; + let mat2: Vec<f32> = vec![0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.]; + let target: Vec<f32> = 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<f32> = vec![1., 10., 100., 1000., 10000., 100000.]; + let mat2: Vec<f32> = vec![10., 100., 1000., 10000., 100000., 1000000.]; + let target: Vec<f32> = 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<f32> = 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<f32> = vec![0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.]; + let mat2: Vec<f32> = vec![1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1.]; + let target: Vec<f32> = 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<f32> = 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<f32> = 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<f32> = vec![ + 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); + let result = interpolate(&mat1, &mat2, ratio); + assert_mat_compare(&result, &target, I32F32::from_num(0)); + + let target: Vec<f32> = 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<(u16, I32F32)>> = vec![vec![]]; + let mat2: Vec<Vec<(u16, I32F32)>> = vec![vec![]]; + let target: Vec<Vec<(u16, I32F32)>> = 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<f32> = vec![0.]; + let mat2: Vec<f32> = vec![1.]; + let target: Vec<f32> = 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<f32> = 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<f32> = 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<f32> = vec![0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.]; + let mat2: Vec<f32> = vec![0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.]; + let target: Vec<f32> = 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<f32> = vec![1., 0., 100., 1000., 10000., 100000.]; + let mat2: Vec<f32> = vec![10., 100., 1000., 10000., 100000., 0.]; + let target: Vec<f32> = 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<f32> = 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<f32> = vec![0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.]; + let mat2: Vec<f32> = vec![1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1.]; + let target: Vec<f32> = 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<f32> = 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<f32> = 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<f32> = vec![ + 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); + let result = interpolate_sparse(&mat1, &mat2, 3, ratio); + assert_sparse_mat_compare(&result, &target, I32F32::from_num(0)); + + let target: Vec<f32> = 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<f32> = 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 9731113e5..9d5f939f6 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<T: Config> Pallet<T> { Self::deposit_event(Event::BondsMovingAverageSet(netuid, bonds_moving_average)); } + pub fn get_bonds_penalty(netuid: u16) -> u16 { + BondsPenalty::<T>::get(netuid) + } + pub fn set_bonds_penalty(netuid: u16, bonds_penalty: u16) { + BondsPenalty::<T>::insert(netuid, bonds_penalty); + Self::deposit_event(Event::BondsPenaltySet(netuid, bonds_penalty)); + } + pub fn get_max_registrations_per_block(netuid: u16) -> u16 { MaxRegistrationsPerBlock::<T>::get(netuid) } diff --git a/runtime/src/lib.rs b/runtime/src/lib.rs index 36a879e48..e0ea63987 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 a0819b4d5bb2806a771ba98c0f1a2274b0183cbb Mon Sep 17 00:00:00 2001 From: open-junius <zhou@opentensor.dev> Date: Fri, 24 Jan 2025 00:43:02 +0800 Subject: [PATCH 055/145] remove byte_to_account_id --- runtime/src/precompiles/balance_transfer.rs | 6 +++--- runtime/src/precompiles/mod.rs | 12 ------------ 2 files changed, 3 insertions(+), 15 deletions(-) diff --git a/runtime/src/precompiles/balance_transfer.rs b/runtime/src/precompiles/balance_transfer.rs index 99d911b02..895e91a62 100644 --- a/runtime/src/precompiles/balance_transfer.rs +++ b/runtime/src/precompiles/balance_transfer.rs @@ -9,7 +9,7 @@ use sp_std::vec; use crate::{Runtime, RuntimeCall}; -use crate::precompiles::{bytes_to_account_id, get_method_id, get_slice}; +use crate::precompiles::{get_method_id, get_pubkey, get_slice}; pub const BALANCE_TRANSFER_INDEX: u64 = 2048; @@ -47,8 +47,8 @@ impl BalanceTransferPrecompile { 0x62, 0x93, 0x70, 0x5d, ]; let address_bytes_dst: &[u8] = get_slice(txdata, 4, 36)?; - let account_id_src = bytes_to_account_id(&ADDRESS_BYTES_SRC)?; - let account_id_dst = bytes_to_account_id(address_bytes_dst)?; + let (account_id_src, _) = get_pubkey(&ADDRESS_BYTES_SRC)?; + let (account_id_dst, _) = get_pubkey(address_bytes_dst)?; let call = RuntimeCall::Balances(pallet_balances::Call::<Runtime>::transfer_allow_death { diff --git a/runtime/src/precompiles/mod.rs b/runtime/src/precompiles/mod.rs index de4f4fc1f..567e7836a 100644 --- a/runtime/src/precompiles/mod.rs +++ b/runtime/src/precompiles/mod.rs @@ -124,18 +124,6 @@ pub fn get_method_id(method_signature: &str) -> [u8; 4] { [hash[0], hash[1], hash[2], hash[3]] } -/// Convert bytes to AccountId32 with PrecompileFailure as Error -/// which consumes all gas -/// -pub fn bytes_to_account_id(account_id_bytes: &[u8]) -> Result<AccountId32, PrecompileFailure> { - AccountId32::try_from(account_id_bytes).map_err(|_| { - log::info!("Error parsing account id bytes {:?}", account_id_bytes); - PrecompileFailure::Error { - exit_status: ExitError::InvalidRange, - } - }) -} - /// Takes a slice from bytes with PrecompileFailure as Error /// pub fn get_slice(data: &[u8], from: usize, to: usize) -> Result<&[u8], PrecompileFailure> { From 2633e8854606ebad2143179482d2e92d35c3eaf0 Mon Sep 17 00:00:00 2001 From: Cameron Fairchild <cameron@opentensor.ai> Date: Thu, 23 Jan 2025 11:54:43 -0500 Subject: [PATCH 056/145] fix tests to incl fee (#1186) --- pallets/subtensor/src/tests/move_stake.rs | 41 +++++++++++++++++++---- 1 file changed, 34 insertions(+), 7 deletions(-) diff --git a/pallets/subtensor/src/tests/move_stake.rs b/pallets/subtensor/src/tests/move_stake.rs index 04f79f3f9..96d54ed77 100644 --- a/pallets/subtensor/src/tests/move_stake.rs +++ b/pallets/subtensor/src/tests/move_stake.rs @@ -1080,6 +1080,8 @@ fn test_do_swap_success() { 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 fee = DefaultMinStake::<Test>::get(); + let coldkey = U256::from(1); let hotkey = U256::from(2); let stake_amount = DefaultMinStake::<Test>::get() * 10; @@ -1114,7 +1116,11 @@ fn test_do_swap_success() { &coldkey, destination_netuid, ); - assert_abs_diff_eq!(alpha_after, stake_amount, epsilon = stake_amount / 1000); + assert_abs_diff_eq!( + alpha_after, + stake_amount - fee, + epsilon = stake_amount / 1000 + ); }); } @@ -1259,6 +1265,8 @@ fn test_do_swap_same_subnet() { let subnet_owner_hotkey = U256::from(1101); let netuid = add_dynamic_network(&subnet_owner_hotkey, &subnet_owner_coldkey); + let fee = DefaultMinStake::<Test>::get(); + let coldkey = U256::from(1); let hotkey = U256::from(2); let stake_amount = DefaultMinStake::<Test>::get() * 10; @@ -1268,6 +1276,7 @@ fn test_do_swap_same_subnet() { let alpha_before = SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet(&hotkey, &coldkey, netuid); + let fee_as_alpha = SubtensorModule::swap_tao_for_alpha(netuid, fee); assert_ok!(SubtensorModule::do_swap_stake( RuntimeOrigin::signed(coldkey), @@ -1279,7 +1288,8 @@ fn test_do_swap_same_subnet() { let alpha_after = SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet(&hotkey, &coldkey, netuid); - assert_abs_diff_eq!(alpha_after, alpha_before, epsilon = 5); + assert_abs_diff_eq!(alpha_after, alpha_before - fee_as_alpha, epsilon = 15); + // Some slippage due to fee on same subnet. }); } @@ -1291,6 +1301,8 @@ fn test_do_swap_partial_stake() { 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 fee = DefaultMinStake::<Test>::get(); + let coldkey = U256::from(1); let hotkey = U256::from(2); let total_stake = DefaultMinStake::<Test>::get() * 10; @@ -1298,6 +1310,7 @@ fn test_do_swap_partial_stake() { SubtensorModule::create_account_if_non_existent(&coldkey, &hotkey); SubtensorModule::stake_into_subnet(&hotkey, &coldkey, origin_netuid, total_stake, 0); + let fee_as_alpha2 = SubtensorModule::swap_tao_for_alpha(destination_netuid, fee); let swap_amount = total_stake / 2; assert_ok!(SubtensorModule::do_swap_stake( RuntimeOrigin::signed(coldkey), @@ -1322,7 +1335,7 @@ fn test_do_swap_partial_stake() { &coldkey, destination_netuid ), - swap_amount, + swap_amount - fee_as_alpha2, epsilon = total_stake / 1000 ); }); @@ -1336,6 +1349,8 @@ fn test_do_swap_storage_updates() { 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 fee = DefaultMinStake::<Test>::get(); + let coldkey = U256::from(1); let hotkey = U256::from(2); let stake_amount = DefaultMinStake::<Test>::get() * 10; @@ -1365,13 +1380,15 @@ fn test_do_swap_storage_updates() { 0 ); + let fee_as_alpha = SubtensorModule::swap_tao_for_alpha(destination_netuid, fee); + assert_abs_diff_eq!( SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( &hotkey, &coldkey, destination_netuid ), - alpha, + alpha - fee_as_alpha, epsilon = 5 ); }); @@ -1385,6 +1402,8 @@ fn test_do_swap_multiple_times() { let netuid1 = add_dynamic_network(&subnet_owner_hotkey, &subnet_owner_coldkey); let netuid2 = add_dynamic_network(&subnet_owner_hotkey, &subnet_owner_coldkey); + let fee = DefaultMinStake::<Test>::get(); + let coldkey = U256::from(1); let hotkey = U256::from(2); let initial_stake = DefaultMinStake::<Test>::get() * 10; @@ -1392,6 +1411,7 @@ fn test_do_swap_multiple_times() { SubtensorModule::create_account_if_non_existent(&coldkey, &hotkey); SubtensorModule::stake_into_subnet(&hotkey, &coldkey, netuid1, initial_stake, 0); + let mut total_alpha1_fee = 0; for _ in 0..3 { let alpha1 = SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( &hotkey, &coldkey, netuid1, @@ -1404,6 +1424,9 @@ fn test_do_swap_multiple_times() { netuid2, alpha1 )); + + let fee_as_alpha = SubtensorModule::swap_tao_for_alpha(netuid1, fee); + total_alpha1_fee += fee_as_alpha; } let alpha2 = SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( &hotkey, &coldkey, netuid2, @@ -1416,17 +1439,21 @@ fn test_do_swap_multiple_times() { netuid1, alpha2 )); + + let fee_as_alpha = SubtensorModule::swap_tao_for_alpha(netuid1, fee); + total_alpha1_fee += fee_as_alpha; } } - let final_stake_netuid1 = + let final_stake_netuid1: u64 = SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet(&hotkey, &coldkey, netuid1); let final_stake_netuid2 = SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet(&hotkey, &coldkey, netuid2); + let expected_stake = initial_stake - total_alpha1_fee; assert_abs_diff_eq!( final_stake_netuid1, - initial_stake, - epsilon = initial_stake / 1000 + expected_stake, + epsilon = expected_stake / 1000 ); assert_eq!(final_stake_netuid2, 0); }); From 05b5c6b56389b58290280427d60925824554580a Mon Sep 17 00:00:00 2001 From: Aliaksandr Tsurko <ales@opentensor.dev> Date: Thu, 23 Jan 2025 19:38:54 +0100 Subject: [PATCH 057/145] Disable TotalStake check in try-runtime --- pallets/subtensor/src/utils/try_state.rs | 34 +++++++++++++----------- 1 file changed, 18 insertions(+), 16 deletions(-) diff --git a/pallets/subtensor/src/utils/try_state.rs b/pallets/subtensor/src/utils/try_state.rs index 46a3ba5e4..c42e75dfe 100644 --- a/pallets/subtensor/src/utils/try_state.rs +++ b/pallets/subtensor/src/utils/try_state.rs @@ -17,23 +17,25 @@ impl<T: Config> Pallet<T> { pub fn check_accounting_invariants() -> Result<(), sp_runtime::TryRuntimeError> { use frame_support::traits::fungible::Inspect; - // Calculate the total staked amount - let total_staked = SubnetTAO::<T>::iter().fold(0u64, |acc, (netuid, stake)| { - let acc = acc.saturating_add(stake); + // Disabled: https://github.com/opentensor/subtensor/pull/1166 + // + // // Calculate the total staked amount + // let total_staked = SubnetTAO::<T>::iter().fold(0u64, |acc, (netuid, stake)| { + // let acc = acc.saturating_add(stake); - if netuid == Self::get_root_netuid() { - // root network doesn't have initial pool TAO - acc - } else { - acc.saturating_sub(POOL_INITIAL_TAO) - } - }); + // if netuid == Self::get_root_netuid() { + // // root network doesn't have initial pool TAO + // acc + // } else { + // acc.saturating_sub(POOL_INITIAL_TAO) + // } + // }); - // Verify that the calculated total stake matches the stored TotalStake - ensure!( - total_staked == TotalStake::<T>::get(), - "TotalStake does not match total staked", - ); + // // Verify that the calculated total stake matches the stored TotalStake + // ensure!( + // total_staked == TotalStake::<T>::get(), + // "TotalStake does not match total staked", + // ); // Get the total subnet locked amount let total_subnet_locked = Self::get_total_subnet_locked(); @@ -43,7 +45,7 @@ impl<T: Config> Pallet<T> { // Calculate the expected total issuance let expected_total_issuance = currency_issuance - .saturating_add(total_staked) + .saturating_add(TotalStake::<T>::get()) .saturating_add(total_subnet_locked); // Verify the diff between calculated TI and actual TI is less than delta From 4163b91aae39940215fb75e2aded504b498466c3 Mon Sep 17 00:00:00 2001 From: Aliaksandr Tsurko <ales@opentensor.dev> Date: Thu, 23 Jan 2025 19:43:57 +0100 Subject: [PATCH 058/145] Reformat --- pallets/subtensor/src/utils/try_state.rs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/pallets/subtensor/src/utils/try_state.rs b/pallets/subtensor/src/utils/try_state.rs index c42e75dfe..c2292eeee 100644 --- a/pallets/subtensor/src/utils/try_state.rs +++ b/pallets/subtensor/src/utils/try_state.rs @@ -1,5 +1,3 @@ -#[cfg(feature = "try-runtime")] -use super::subnets::subnet::POOL_INITIAL_TAO; use super::*; impl<T: Config> Pallet<T> { @@ -17,8 +15,8 @@ impl<T: Config> Pallet<T> { pub fn check_accounting_invariants() -> Result<(), sp_runtime::TryRuntimeError> { use frame_support::traits::fungible::Inspect; - // Disabled: https://github.com/opentensor/subtensor/pull/1166 - // + // Disabled: https://github.com/opentensor/subtensor/pull/1166 + // // // Calculate the total staked amount // let total_staked = SubnetTAO::<T>::iter().fold(0u64, |acc, (netuid, stake)| { // let acc = acc.saturating_add(stake); From a58b7707ff83d6579552c85ef668da8881f8f1e9 Mon Sep 17 00:00:00 2001 From: camfairchild <cameron@opentensor.dev> Date: Thu, 23 Jan 2025 18:11:25 -0500 Subject: [PATCH 059/145] add test for just CHK div distr --- pallets/subtensor/src/tests/children.rs | 223 ++++++++++++++++++++++++ 1 file changed, 223 insertions(+) diff --git a/pallets/subtensor/src/tests/children.rs b/pallets/subtensor/src/tests/children.rs index eb299d88e..70f9684a6 100644 --- a/pallets/subtensor/src/tests/children.rs +++ b/pallets/subtensor/src/tests/children.rs @@ -3413,6 +3413,229 @@ fn test_parent_child_chain_emission() { }); } +// 45: Test dividend distribution with children +// This test verifies the correct distribution of emissions in a chain of parent-child relationships: +// - Sets up a network with three neurons A, B, and C in a chain (A -> B -> C) +// - Establishes parent-child relationships with different stake proportions +// - Adds a childkey take for both B and C +// - Distributes emission across each hotkey using a the helper +// - Checks the emission distribution among A, B, and C +// SKIP_WASM_BUILD=1 RUST_LOG=debug cargo test --package pallet-subtensor --lib -- tests::children::test_dividend_distribution_with_children --exact --show-output +#[test] +fn test_dividend_distribution_with_children() { + new_test_ext(1).execute_with(|| { + let netuid: u16 = 1; + add_network(netuid, 1, 0); + // Set owner cut to 0 + SubtensorModule::set_subnet_owner_cut(0_u16); + + // Define hotkeys and coldkeys + let hotkey_a: U256 = U256::from(1); + let hotkey_b: U256 = U256::from(2); + let hotkey_c: U256 = U256::from(3); + let coldkey_a: U256 = U256::from(100); + let coldkey_b: U256 = U256::from(101); + let coldkey_c: U256 = U256::from(102); + + // Register neurons with decreasing stakes + register_ok_neuron(netuid, hotkey_a, coldkey_a, 0); + register_ok_neuron(netuid, hotkey_b, coldkey_b, 0); + register_ok_neuron(netuid, hotkey_c, coldkey_c, 0); + + // Add initial stakes + SubtensorModule::add_balance_to_coldkey_account(&coldkey_a, 1_000); + SubtensorModule::add_balance_to_coldkey_account(&coldkey_b, 1_000); + SubtensorModule::add_balance_to_coldkey_account(&coldkey_c, 1_000); + + // Swap to alpha + let total_tao: I96F32 = I96F32::from_num(300_000 + 100_000 + 50_000); + let total_alpha: I96F32 = I96F32::from_num(SubtensorModule::swap_tao_for_alpha( + netuid, + total_tao.saturating_to_num::<u64>(), + )); + + // Set the stakes directly + // This avoids needing to swap tao to alpha, impacting the initial stake distribution. + SubtensorModule::increase_stake_for_hotkey_and_coldkey_on_subnet( + &hotkey_a, + &coldkey_a, + netuid, + (total_alpha * I96F32::from_num(300_000) / total_tao).saturating_to_num::<u64>(), + ); + SubtensorModule::increase_stake_for_hotkey_and_coldkey_on_subnet( + &hotkey_b, + &coldkey_b, + netuid, + (total_alpha * I96F32::from_num(100_000) / total_tao).saturating_to_num::<u64>(), + ); + SubtensorModule::increase_stake_for_hotkey_and_coldkey_on_subnet( + &hotkey_c, + &coldkey_c, + netuid, + (total_alpha * I96F32::from_num(50_000) / total_tao).saturating_to_num::<u64>(), + ); + + // Get old stakes + let stake_a: u64 = SubtensorModule::get_total_stake_for_hotkey(&hotkey_a); + let stake_b: u64 = SubtensorModule::get_total_stake_for_hotkey(&hotkey_b); + let stake_c: u64 = SubtensorModule::get_total_stake_for_hotkey(&hotkey_c); + + // Assert initial stake is correct + let rel_stake_a = I96F32::from_num(stake_a) / total_tao; + let rel_stake_b = I96F32::from_num(stake_b) / total_tao; + let rel_stake_c = I96F32::from_num(stake_c) / total_tao; + + log::info!("rel_stake_a: {:?}", rel_stake_a); // 0.6666 -> 2/3 + log::info!("rel_stake_b: {:?}", rel_stake_b); // 0.2222 -> 2/9 + log::info!("rel_stake_c: {:?}", rel_stake_c); // 0.1111 -> 1/9 + assert_eq!(rel_stake_a, I96F32::from_num(300_000) / total_tao); + assert_eq!(rel_stake_b, I96F32::from_num(100_000) / total_tao); + assert_eq!(rel_stake_c, I96F32::from_num(50_000) / total_tao); + + // Set parent-child relationships + // A -> B (50% of A's stake) + mock_set_children(&coldkey_a, &hotkey_a, netuid, &[(u64::MAX / 2, hotkey_b)]); + + // B -> C (50% of B's stake) + mock_set_children(&coldkey_b, &hotkey_b, netuid, &[(u64::MAX / 2, hotkey_c)]); + + // Set CHK take rate to 1/9 + let chk_take: I96F32 = I96F32::from_num(1_f64 / 9_f64); + let chk_take_u16: u16 = (chk_take * I96F32::from_num(u16::MAX)).saturating_to_num::<u16>(); + ChildkeyTake::<Test>::insert(hotkey_b, netuid, chk_take_u16); + ChildkeyTake::<Test>::insert(hotkey_c, netuid, chk_take_u16); + + // Set the weight of root TAO to be 0%, so only alpha is effective. + SubtensorModule::set_tao_weight(0); + + let hardcoded_emission: I96F32 = I96F32::from_num(1_000_000); // 1 million (adjust as needed) + + let hotkey_emission: Vec<(U256, u64, u64)> = + SubtensorModule::epoch(netuid, hardcoded_emission.saturating_to_num::<u64>()); + log::info!("hotkey_emission: {:?}", hotkey_emission); + let total_emission: I96F32 = hotkey_emission + .iter() + .map(|(_, _, emission)| I96F32::from_num(*emission)) + .sum(); + + // Verify emissions match expected from CHK arrangements + let em_eps: I96F32 = I96F32::from_num(1e-4); // 4 decimal places + // A's pending emission: + assert!( + ((I96F32::from_num(hotkey_emission[0].2) / total_emission) - + I96F32::from_num(2_f64 / 3_f64 * 1_f64 / 2_f64)).abs() // 2/3 * 1/2 = 1/3; 50% -> B + <= em_eps, + "A should have pending emission of 1/3 of total emission" + ); + // B's pending emission: + assert!( + ((I96F32::from_num(hotkey_emission[1].2) / total_emission) - + (I96F32::from_num(2_f64 / 9_f64 * 1_f64 / 2_f64 + 2_f64 / 3_f64 * 1_f64 / 2_f64))).abs() // 2/9 * 1/2 + 2/3 * 1/2; 50% -> C + 50% from A + <= em_eps, + "B should have pending emission of 4/9 of total emission" + ); + // C's pending emission: + assert!( + ((I96F32::from_num(hotkey_emission[2].2) / total_emission) - + (I96F32::from_num(1_f64 / 9_f64 + 1_f64 / 2_f64 * 2_f64 / 9_f64))).abs() // 1/9 + 2/9 * 1/2; 50% from B + <= em_eps, + "C should have pending emission of 1/9 of total emission" + ); + + let dividends_a = SubtensorModule::get_dividends_distribution( + &hotkey_a, + netuid, + hardcoded_emission.saturating_to_num::<u64>(), + ); + let dividends_b = SubtensorModule::get_dividends_distribution( + &hotkey_b, + netuid, + hardcoded_emission.saturating_to_num::<u64>(), + ); + let dividends_c = SubtensorModule::get_dividends_distribution( + &hotkey_c, + netuid, + hardcoded_emission.saturating_to_num::<u64>(), + ); + log::info!("dividends_a: {:?}", dividends_a); + log::info!("dividends_b: {:?}", dividends_b); + log::info!("dividends_c: {:?}", dividends_c); + + // We expect A to get all of its own emission, as it has no parents. + assert_eq!(dividends_a.len(), 1); + assert_eq!(dividends_a[0].0, hotkey_a); + assert_eq!( + dividends_a[0].1, + hardcoded_emission.saturating_to_num::<u64>() + ); + assert_abs_diff_eq!( + dividends_a + .iter() + .map(|(_, emission)| *emission) + .sum::<u64>(), + hardcoded_emission.saturating_to_num::<u64>(), + epsilon = (hardcoded_emission / 1000).saturating_to_num::<u64>() + ); + + // We expect B to get a portion of its own emission, and some comission from A, where A gets the rest. + // B re-delegates 0.5 of its stake to C; And A re-delegates 0.5 of its stake to B. + let total_stake_b = rel_stake_b * 1 / 2 + rel_stake_a * 1 / 2; + let expected_b_b: u64 = ((rel_stake_b * 1 / 2) / total_stake_b * hardcoded_emission + + (rel_stake_a * 1 / 2) / total_stake_b * hardcoded_emission * chk_take) + .saturating_to_num::<u64>(); + assert_eq!(dividends_b.len(), 2); // A and B + assert_eq!(dividends_b[1].0, hotkey_b); + assert_abs_diff_eq!( + dividends_b[1].1, + expected_b_b, + epsilon = (hardcoded_emission / 1000).saturating_to_num::<u64>() + ); + let expected_b_a: u64 = hardcoded_emission.saturating_to_num::<u64>() - expected_b_b; + assert_eq!(dividends_b[0].0, hotkey_a); + assert_abs_diff_eq!( + dividends_b[0].1, + expected_b_a, + epsilon = (hardcoded_emission / 1000).saturating_to_num::<u64>() + ); + assert_abs_diff_eq!( + dividends_b + .iter() + .map(|(_, emission)| *emission) + .sum::<u64>(), + hardcoded_emission.saturating_to_num::<u64>(), + epsilon = (hardcoded_emission / 1000).saturating_to_num::<u64>() + ); + + // We expect C to get a portion of its own emission, and some comission from B, where B gets the rest. + let total_stake_c = rel_stake_c + rel_stake_b * 1 / 2; + let expected_c_c: u64 = (rel_stake_c / total_stake_c * hardcoded_emission + + (rel_stake_b * 1 / 2) / total_stake_c * hardcoded_emission * chk_take) + .saturating_to_num::<u64>(); + assert_eq!(dividends_c.len(), 2); // B and C + assert_eq!(dividends_c[1].0, hotkey_c); + assert_abs_diff_eq!( + dividends_c[1].1, + expected_c_c, + epsilon = (hardcoded_emission / 1000).saturating_to_num::<u64>() + ); + let expected_c_b: u64 = hardcoded_emission.saturating_to_num::<u64>() - expected_c_c; + assert_eq!(dividends_c[0].0, hotkey_b); + assert_abs_diff_eq!( + dividends_c[0].1, + expected_c_b, + epsilon = (hardcoded_emission / 1000).saturating_to_num::<u64>() + ); + assert_abs_diff_eq!( + dividends_c + .iter() + .map(|(_, emission)| *emission) + .sum::<u64>(), + hardcoded_emission.saturating_to_num::<u64>(), + epsilon = (hardcoded_emission / 1000).saturating_to_num::<u64>() + ); + }); +} + // 46: Test emission distribution when adding/removing parent-child relationships mid-epoch // This test verifies the correct distribution of emissions when parent-child relationships change: // - Sets up a network with three neurons: parent, child1, and child2 From d83388caba07b5d9aadf0d5d7c19a5839bf2dd89 Mon Sep 17 00:00:00 2001 From: camfairchild <cameron@opentensor.dev> Date: Thu, 23 Jan 2025 18:15:57 -0500 Subject: [PATCH 060/145] separate out test for epoch with CHK --- pallets/subtensor/src/tests/children.rs | 167 +++++++++++++++++++----- 1 file changed, 133 insertions(+), 34 deletions(-) diff --git a/pallets/subtensor/src/tests/children.rs b/pallets/subtensor/src/tests/children.rs index 70f9684a6..62e8ceecc 100644 --- a/pallets/subtensor/src/tests/children.rs +++ b/pallets/subtensor/src/tests/children.rs @@ -3312,38 +3312,6 @@ fn test_parent_child_chain_emission() { let hardcoded_emission: I96F32 = I96F32::from_num(1_000_000); // 1 million (adjust as needed) - let hotkey_emission: Vec<(U256, u64, u64)> = - SubtensorModule::epoch(netuid, hardcoded_emission.saturating_to_num::<u64>()); - log::info!("hotkey_emission: {:?}", hotkey_emission); - let total_emission: I96F32 = hotkey_emission - .iter() - .map(|(_, _, emission)| I96F32::from_num(*emission)) - .sum(); - - // Verify emissions match expected from CHK arrangements - let em_eps: I96F32 = I96F32::from_num(1e-4); // 4 decimal places - // A's pending emission: - assert!( - ((I96F32::from_num(hotkey_emission[0].2) / total_emission) - - I96F32::from_num(2_f64 / 3_f64 * 1_f64 / 2_f64)).abs() // 2/3 * 1/2 = 1/3; 50% -> B - <= em_eps, - "A should have pending emission of 1/3 of total emission" - ); - // B's pending emission: - assert!( - ((I96F32::from_num(hotkey_emission[1].2) / total_emission) - - (I96F32::from_num(2_f64 / 9_f64 * 1_f64 / 2_f64 + 2_f64 / 3_f64 * 1_f64 / 2_f64))).abs() // 2/9 * 1/2 + 2/3 * 1/2; 50% -> C + 50% from A - <= em_eps, - "B should have pending emission of 4/9 of total emission" - ); - // C's pending emission: - assert!( - ((I96F32::from_num(hotkey_emission[2].2) / total_emission) - - (I96F32::from_num(1_f64 / 9_f64 + 1_f64 / 2_f64 * 2_f64 / 9_f64))).abs() // 1/9 + 2/9 * 1/2; 50% from B - <= em_eps, - "C should have pending emission of 1/9 of total emission" - ); - // Run epoch with a hardcoded emission value SubtensorModule::run_coinbase(hardcoded_emission); @@ -3413,7 +3381,138 @@ fn test_parent_child_chain_emission() { }); } -// 45: Test dividend distribution with children +// 45: Test *epoch* with a chain of parent-child relationships (e.g., A -> B -> C) +// This test verifies the correct distribution of emissions in a chain of parent-child relationships: +// - Sets up a network with three neurons A, B, and C in a chain (A -> B -> C) +// - Establishes parent-child relationships with different stake proportions +// - Sets weights for all neurons +// - Runs an epoch with a hardcoded emission value +// - Checks the emission distribution among A, B, and C +// SKIP_WASM_BUILD=1 RUST_LOG=debug cargo test --package pallet-subtensor --lib -- tests::children::test_parent_child_chain_epoch --exact --show-output +#[test] +fn test_parent_child_chain_epoch() { + new_test_ext(1).execute_with(|| { + let netuid: u16 = 1; + add_network(netuid, 1, 0); + // Set owner cut to 0 + SubtensorModule::set_subnet_owner_cut(0_u16); + + // Define hotkeys and coldkeys + let hotkey_a: U256 = U256::from(1); + let hotkey_b: U256 = U256::from(2); + let hotkey_c: U256 = U256::from(3); + let coldkey_a: U256 = U256::from(100); + let coldkey_b: U256 = U256::from(101); + let coldkey_c: U256 = U256::from(102); + + // Register neurons with decreasing stakes + register_ok_neuron(netuid, hotkey_a, coldkey_a, 0); + register_ok_neuron(netuid, hotkey_b, coldkey_b, 0); + register_ok_neuron(netuid, hotkey_c, coldkey_c, 0); + + // Add initial stakes + SubtensorModule::add_balance_to_coldkey_account(&coldkey_a, 1_000); + SubtensorModule::add_balance_to_coldkey_account(&coldkey_b, 1_000); + SubtensorModule::add_balance_to_coldkey_account(&coldkey_c, 1_000); + + // Swap to alpha + let total_tao: I96F32 = I96F32::from_num(300_000 + 100_000 + 50_000); + let total_alpha: I96F32 = I96F32::from_num(SubtensorModule::swap_tao_for_alpha( + netuid, + total_tao.saturating_to_num::<u64>(), + )); + + // Set the stakes directly + // This avoids needing to swap tao to alpha, impacting the initial stake distribution. + SubtensorModule::increase_stake_for_hotkey_and_coldkey_on_subnet( + &hotkey_a, + &coldkey_a, + netuid, + (total_alpha * I96F32::from_num(300_000) / total_tao).saturating_to_num::<u64>(), + ); + SubtensorModule::increase_stake_for_hotkey_and_coldkey_on_subnet( + &hotkey_b, + &coldkey_b, + netuid, + (total_alpha * I96F32::from_num(100_000) / total_tao).saturating_to_num::<u64>(), + ); + SubtensorModule::increase_stake_for_hotkey_and_coldkey_on_subnet( + &hotkey_c, + &coldkey_c, + netuid, + (total_alpha * I96F32::from_num(50_000) / total_tao).saturating_to_num::<u64>(), + ); + + // Get old stakes + let stake_a: u64 = SubtensorModule::get_total_stake_for_hotkey(&hotkey_a); + let stake_b: u64 = SubtensorModule::get_total_stake_for_hotkey(&hotkey_b); + let stake_c: u64 = SubtensorModule::get_total_stake_for_hotkey(&hotkey_c); + + // Assert initial stake is correct + let rel_stake_a = I96F32::from_num(stake_a) / total_tao; + let rel_stake_b = I96F32::from_num(stake_b) / total_tao; + let rel_stake_c = I96F32::from_num(stake_c) / total_tao; + + log::info!("rel_stake_a: {:?}", rel_stake_a); // 0.6666 -> 2/3 + log::info!("rel_stake_b: {:?}", rel_stake_b); // 0.2222 -> 2/9 + log::info!("rel_stake_c: {:?}", rel_stake_c); // 0.1111 -> 1/9 + assert_eq!(rel_stake_a, I96F32::from_num(300_000) / total_tao); + assert_eq!(rel_stake_b, I96F32::from_num(100_000) / total_tao); + assert_eq!(rel_stake_c, I96F32::from_num(50_000) / total_tao); + + // Set parent-child relationships + // A -> B (50% of A's stake) + mock_set_children(&coldkey_a, &hotkey_a, netuid, &[(u64::MAX / 2, hotkey_b)]); + + // B -> C (50% of B's stake) + mock_set_children(&coldkey_b, &hotkey_b, netuid, &[(u64::MAX / 2, hotkey_c)]); + + // Set CHK take rate to 1/9 + let chk_take: I96F32 = I96F32::from_num(1_f64 / 9_f64); + let chk_take_u16: u16 = (chk_take * I96F32::from_num(u16::MAX)).saturating_to_num::<u16>(); + ChildkeyTake::<Test>::insert(hotkey_b, netuid, chk_take_u16); + ChildkeyTake::<Test>::insert(hotkey_c, netuid, chk_take_u16); + + // Set the weight of root TAO to be 0%, so only alpha is effective. + SubtensorModule::set_tao_weight(0); + + let hardcoded_emission: I96F32 = I96F32::from_num(1_000_000); // 1 million (adjust as needed) + + let hotkey_emission: Vec<(U256, u64, u64)> = + SubtensorModule::epoch(netuid, hardcoded_emission.saturating_to_num::<u64>()); + log::info!("hotkey_emission: {:?}", hotkey_emission); + let total_emission: I96F32 = hotkey_emission + .iter() + .map(|(_, _, emission)| I96F32::from_num(*emission)) + .sum(); + + // Verify emissions match expected from CHK arrangements + let em_eps: I96F32 = I96F32::from_num(1e-4); // 4 decimal places + // A's pending emission: + assert!( + ((I96F32::from_num(hotkey_emission[0].2) / total_emission) - + I96F32::from_num(2_f64 / 3_f64 * 1_f64 / 2_f64)).abs() // 2/3 * 1/2 = 1/3; 50% -> B + <= em_eps, + "A should have pending emission of 1/3 of total emission" + ); + // B's pending emission: + assert!( + ((I96F32::from_num(hotkey_emission[1].2) / total_emission) - + (I96F32::from_num(2_f64 / 9_f64 * 1_f64 / 2_f64 + 2_f64 / 3_f64 * 1_f64 / 2_f64))).abs() // 2/9 * 1/2 + 2/3 * 1/2; 50% -> C + 50% from A + <= em_eps, + "B should have pending emission of 4/9 of total emission" + ); + // C's pending emission: + assert!( + ((I96F32::from_num(hotkey_emission[2].2) / total_emission) - + (I96F32::from_num(1_f64 / 9_f64 + 1_f64 / 2_f64 * 2_f64 / 9_f64))).abs() // 1/9 + 2/9 * 1/2; 50% from B + <= em_eps, + "C should have pending emission of 1/9 of total emission" + ); + }); +} + +// 46: Test dividend distribution with children // This test verifies the correct distribution of emissions in a chain of parent-child relationships: // - Sets up a network with three neurons A, B, and C in a chain (A -> B -> C) // - Establishes parent-child relationships with different stake proportions @@ -3636,7 +3735,7 @@ fn test_dividend_distribution_with_children() { }); } -// 46: Test emission distribution when adding/removing parent-child relationships mid-epoch +// 47: Test emission distribution when adding/removing parent-child relationships mid-epoch // This test verifies the correct distribution of emissions when parent-child relationships change: // - Sets up a network with three neurons: parent, child1, and child2 // - Establishes initial parent-child relationship between parent and child1 From 77a9aab3b1d1172a195fd46dc9bf764e844ef24f Mon Sep 17 00:00:00 2001 From: Greg Zaitsev <gregz@opentensor.dev> Date: Thu, 23 Jan 2025 18:48:22 -0500 Subject: [PATCH 061/145] Fix tx validation for staking extrinsics --- pallets/subtensor/src/lib.rs | 184 +++++++++++--- pallets/subtensor/src/macros/dispatches.rs | 4 +- pallets/subtensor/src/macros/errors.rs | 4 +- pallets/subtensor/src/staking/add_stake.rs | 24 +- pallets/subtensor/src/staking/move_stake.rs | 240 ++++++------------ pallets/subtensor/src/staking/remove_stake.rs | 27 +- pallets/subtensor/src/staking/stake_utils.rs | 122 +++++++++ pallets/subtensor/src/tests/move_stake.rs | 7 +- pallets/subtensor/src/tests/registration.rs | 12 +- pallets/subtensor/src/tests/staking.rs | 37 +-- pallets/subtensor/src/tests/weights.rs | 12 +- runtime/src/precompiles/staking.rs | 2 +- runtime/tests/pallet_proxy.rs | 4 +- 13 files changed, 410 insertions(+), 269 deletions(-) diff --git a/pallets/subtensor/src/lib.rs b/pallets/subtensor/src/lib.rs index 1a3f0e27b..ec23c64bf 100644 --- a/pallets/subtensor/src/lib.rs +++ b/pallets/subtensor/src/lib.rs @@ -12,10 +12,7 @@ use frame_support::{ dispatch::{self, DispatchInfo, DispatchResult, DispatchResultWithPostInfo, PostDispatchInfo}, ensure, pallet_macros::import_section, - traits::{ - tokens::{fungible, Fortitude, Preservation}, - IsSubType, - }, + traits::{tokens::fungible, IsSubType}, }; use codec::{Decode, Encode}; @@ -1552,6 +1549,11 @@ pub enum CustomTransactionError { ColdkeyInSwapSchedule, StakeAmountTooLow, BalanceTooLow, + SubnetDoesntExist, + HotkeyAccountDoesntExist, + NotEnoughStakeToWithdraw, + RateLimitExceeded, + BadRequest, } impl From<CustomTransactionError> for u8 { @@ -1560,6 +1562,11 @@ impl From<CustomTransactionError> for u8 { CustomTransactionError::ColdkeyInSwapSchedule => 0, CustomTransactionError::StakeAmountTooLow => 1, CustomTransactionError::BalanceTooLow => 2, + CustomTransactionError::SubnetDoesntExist => 3, + CustomTransactionError::HotkeyAccountDoesntExist => 4, + CustomTransactionError::NotEnoughStakeToWithdraw => 5, + CustomTransactionError::RateLimitExceeded => 6, + CustomTransactionError::BadRequest => 255, } } } @@ -1602,6 +1609,41 @@ where pub fn check_weights_min_stake(who: &T::AccountId, netuid: u16) -> bool { Pallet::<T>::check_weights_min_stake(who, netuid) } + + pub fn result_to_validity(result: Result<(), Error<T>>) -> TransactionValidity { + if let Err(err) = result { + match err { + Error::<T>::AmountTooLow => Err(InvalidTransaction::Custom( + CustomTransactionError::StakeAmountTooLow.into(), + ) + .into()), + Error::<T>::SubnetNotExists => Err(InvalidTransaction::Custom( + CustomTransactionError::SubnetDoesntExist.into(), + ) + .into()), + Error::<T>::NotEnoughBalanceToStake => Err(InvalidTransaction::Custom( + CustomTransactionError::BalanceTooLow.into(), + ) + .into()), + Error::<T>::HotKeyAccountNotExists => Err(InvalidTransaction::Custom( + CustomTransactionError::HotkeyAccountDoesntExist.into(), + ) + .into()), + Error::<T>::NotEnoughStakeToWithdraw => Err(InvalidTransaction::Custom( + CustomTransactionError::NotEnoughStakeToWithdraw.into(), + ) + .into()), + _ => Err( + InvalidTransaction::Custom(CustomTransactionError::BadRequest.into()).into(), + ), + } + } else { + Ok(ValidTransaction { + priority: Self::get_priority_vanilla(), + ..Default::default() + }) + } + } } impl<T: Config + Send + Sync + TypeInfo> sp_std::fmt::Debug for SubtensorSignedExtension<T> { @@ -1646,7 +1688,10 @@ where ..Default::default() }) } else { - Err(InvalidTransaction::Custom(1).into()) + Err(InvalidTransaction::Custom( + CustomTransactionError::StakeAmountTooLow.into(), + ) + .into()) } } Some(Call::reveal_weights { netuid, .. }) => { @@ -1658,7 +1703,10 @@ where ..Default::default() }) } else { - Err(InvalidTransaction::Custom(2).into()) + Err(InvalidTransaction::Custom( + CustomTransactionError::StakeAmountTooLow.into(), + ) + .into()) } } Some(Call::batch_reveal_weights { netuid, .. }) => { @@ -1670,7 +1718,10 @@ where ..Default::default() }) } else { - Err(InvalidTransaction::Custom(6).into()) + Err(InvalidTransaction::Custom( + CustomTransactionError::StakeAmountTooLow.into(), + ) + .into()) } } Some(Call::set_weights { netuid, .. }) => { @@ -1682,7 +1733,10 @@ where ..Default::default() }) } else { - Err(InvalidTransaction::Custom(3).into()) + Err(InvalidTransaction::Custom( + CustomTransactionError::StakeAmountTooLow.into(), + ) + .into()) } } Some(Call::set_tao_weights { netuid, hotkey, .. }) => { @@ -1694,7 +1748,10 @@ where ..Default::default() }) } else { - Err(InvalidTransaction::Custom(4).into()) + Err(InvalidTransaction::Custom( + CustomTransactionError::StakeAmountTooLow.into(), + ) + .into()) } } Some(Call::commit_crv3_weights { netuid, .. }) => { @@ -1706,40 +1763,88 @@ where ..Default::default() }) } else { - Err(InvalidTransaction::Custom(7).into()) + Err(InvalidTransaction::Custom( + CustomTransactionError::StakeAmountTooLow.into(), + ) + .into()) } } Some(Call::add_stake { - hotkey: _, - netuid: _, + hotkey, + netuid, amount, }) => { - // Check that amount parameter is at least the min stake - // also check the coldkey balance - let coldkey_balance = <<T as Config>::Currency as fungible::Inspect< - <T as frame_system::Config>::AccountId, - >>::reducible_balance( - who, Preservation::Expendable, Fortitude::Polite - ); - let min_amount = - DefaultMinStake::<T>::get().saturating_add(DefaultStakingFee::<T>::get()); - - if *amount < min_amount { - InvalidTransaction::Custom(CustomTransactionError::StakeAmountTooLow.into()) - .into() - } else if coldkey_balance < min_amount { - InvalidTransaction::Custom(CustomTransactionError::BalanceTooLow.into()).into() - } else { - Ok(ValidTransaction { - priority: Self::get_priority_vanilla(), - ..Default::default() - }) - } + // Fully validate the user input + Self::result_to_validity(Pallet::<T>::validate_add_stake( + who, hotkey, *netuid, *amount, + )) + } + Some(Call::remove_stake { + hotkey, + netuid, + amount_unstaked, + }) => { + // Fully validate the user input + Self::result_to_validity(Pallet::<T>::validate_remove_stake( + who, + hotkey, + *netuid, + *amount_unstaked, + )) + } + Some(Call::move_stake { + origin_hotkey, + destination_hotkey, + origin_netuid, + destination_netuid, + alpha_amount, + }) => { + // Fully validate the user input + Self::result_to_validity(Pallet::<T>::validate_stake_transition( + who, + who, + origin_hotkey, + destination_hotkey, + *origin_netuid, + *destination_netuid, + *alpha_amount, + )) + } + Some(Call::transfer_stake { + destination_coldkey, + hotkey, + origin_netuid, + destination_netuid, + alpha_amount, + }) => { + // Fully validate the user input + Self::result_to_validity(Pallet::<T>::validate_stake_transition( + who, + destination_coldkey, + hotkey, + hotkey, + *origin_netuid, + *destination_netuid, + *alpha_amount, + )) + } + Some(Call::swap_stake { + hotkey, + origin_netuid, + destination_netuid, + alpha_amount, + }) => { + // Fully validate the user input + Self::result_to_validity(Pallet::<T>::validate_stake_transition( + who, + who, + hotkey, + hotkey, + *origin_netuid, + *destination_netuid, + *alpha_amount, + )) } - Some(Call::remove_stake { .. }) => Ok(ValidTransaction { - priority: Self::get_priority_vanilla(), - ..Default::default() - }), Some(Call::register { netuid, .. } | Call::burned_register { netuid, .. }) => { let registrations_this_interval = Pallet::<T>::get_registrations_this_interval(*netuid); @@ -1748,7 +1853,10 @@ where if registrations_this_interval >= (max_registrations_per_interval.saturating_mul(3)) { // If the registration limit for the interval is exceeded, reject the transaction - return Err(InvalidTransaction::Custom(5).into()); + return Err(InvalidTransaction::Custom( + CustomTransactionError::RateLimitExceeded.into(), + ) + .into()); } Ok(ValidTransaction { priority: Self::get_priority_vanilla(), diff --git a/pallets/subtensor/src/macros/dispatches.rs b/pallets/subtensor/src/macros/dispatches.rs index d9eafffa7..2f4e2f563 100644 --- a/pallets/subtensor/src/macros/dispatches.rs +++ b/pallets/subtensor/src/macros/dispatches.rs @@ -584,9 +584,9 @@ mod dispatches { origin: OriginFor<T>, hotkey: T::AccountId, netuid: u16, - amount_staked: u64, + amount: u64, ) -> DispatchResult { - Self::do_add_stake(origin, hotkey, netuid, amount_staked) + Self::do_add_stake(origin, hotkey, netuid, amount) } /// Remove stake from the staking account. The call must be made diff --git a/pallets/subtensor/src/macros/errors.rs b/pallets/subtensor/src/macros/errors.rs index 649336674..dd5e97b78 100644 --- a/pallets/subtensor/src/macros/errors.rs +++ b/pallets/subtensor/src/macros/errors.rs @@ -25,8 +25,8 @@ mod errors { /// Request to stake, unstake or subscribe is made by a coldkey that is not associated with /// the hotkey account. NonAssociatedColdKey, - /// Stake amount to withdraw is zero. - StakeToWithdrawIsZero, + /// DEPRECATED: Stake amount to withdraw is zero. + // StakeToWithdrawIsZero, /// The caller does not have enought stake to perform this action. NotEnoughStake, /// The caller is requesting removing more stake than there exists in the staking account. diff --git a/pallets/subtensor/src/staking/add_stake.rs b/pallets/subtensor/src/staking/add_stake.rs index 763899d6d..326eca1dc 100644 --- a/pallets/subtensor/src/staking/add_stake.rs +++ b/pallets/subtensor/src/staking/add_stake.rs @@ -47,30 +47,14 @@ impl<T: Config> Pallet<T> { stake_to_be_added ); - // 2. Ensure that the subnet exists. - ensure!(Self::if_subnet_exist(netuid), Error::<T>::SubnetNotExists); + // 2. Validate user input + Self::validate_add_stake(&coldkey, &hotkey, netuid, stake_to_be_added)?; - // 3. Ensure the callers coldkey has enough stake to perform the transaction. - ensure!( - Self::can_remove_balance_from_coldkey_account(&coldkey, stake_to_be_added), - Error::<T>::NotEnoughBalanceToStake - ); - - // 4. Ensure that the hotkey account exists this is only possible through registration. - ensure!( - Self::hotkey_account_exists(&hotkey), - Error::<T>::HotKeyAccountNotExists - ); - - // Ensure stake_to_be_added is at least DefaultMinStake plus fee - let min_amount = DefaultMinStake::<T>::get().saturating_add(DefaultStakingFee::<T>::get()); - ensure!(stake_to_be_added >= min_amount, Error::<T>::AmountTooLow); - - // 5. Ensure the remove operation from the coldkey is a success. + // 3. 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)?; - // 6. Swap the stake into alpha on the subnet and increase counters. + // 4. Swap the stake into alpha on the subnet and increase counters. // Emit the staking event. let fee = DefaultStakingFee::<T>::get(); Self::stake_into_subnet(&hotkey, &coldkey, netuid, tao_staked, fee); diff --git a/pallets/subtensor/src/staking/move_stake.rs b/pallets/subtensor/src/staking/move_stake.rs index 9c4ffabff..4644b3213 100644 --- a/pallets/subtensor/src/staking/move_stake.rs +++ b/pallets/subtensor/src/staking/move_stake.rs @@ -31,68 +31,21 @@ impl<T: Config> Pallet<T> { destination_netuid: u16, alpha_amount: u64, ) -> dispatch::DispatchResult { - // --- 1. Check that the origin is signed by the origin_hotkey. + // Check that the origin is signed by the origin_hotkey. let coldkey = ensure_signed(origin)?; - // --- 2. Check that the subnet exists. - ensure!( - Self::if_subnet_exist(origin_netuid), - Error::<T>::SubnetNotExists - ); - ensure!( - Self::if_subnet_exist(destination_netuid), - Error::<T>::SubnetNotExists - ); - - // --- 3. Check that the origin_hotkey exists. - ensure!( - Self::hotkey_account_exists(&origin_hotkey), - Error::<T>::HotKeyAccountNotExists - ); - - // --- 4. Check that the destination_hotkey exists. - ensure!( - Self::hotkey_account_exists(&destination_hotkey), - Error::<T>::HotKeyAccountNotExists - ); - - // --- 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, + // Validate input and move stake + let tao_moved = Self::transition_stake_internal( &coldkey, + &coldkey, + &origin_hotkey, + &destination_hotkey, origin_netuid, - ); - ensure!( - alpha_amount <= origin_alpha, - Error::<T>::NotEnoughStakeToWithdraw - ); - - // --- 7. Unstake the amount of alpha from the origin subnet, converting it to TAO - let fee = DefaultStakingFee::<T>::get().saturating_div(2); // fee is half of min stake because it is applied twice - let origin_tao = Self::unstake_from_subnet( - &origin_hotkey.clone(), - &coldkey.clone(), - origin_netuid, - alpha_amount, - fee, - ); - - // Ensure origin_tao is at least DefaultMinStake - ensure!( - origin_tao.saturating_sub(fee) >= DefaultMinStake::<T>::get(), - Error::<T>::AmountTooLow - ); - - // --- 8. Stake the resulting TAO into the destination subnet for the destination hotkey - Self::stake_into_subnet( - &destination_hotkey.clone(), - &coldkey.clone(), destination_netuid, - origin_tao, - fee, - ); + alpha_amount, + )?; - // --- 9. Log the event. + // Log the event. log::info!( "StakeMoved( coldkey:{:?}, origin_hotkey:{:?}, origin_netuid:{:?}, destination_hotkey:{:?}, destination_netuid:{:?} )", coldkey.clone(), @@ -107,10 +60,10 @@ impl<T: Config> Pallet<T> { origin_netuid, destination_hotkey, destination_netuid, - origin_tao.saturating_sub(fee), + tao_moved, )); - // -- 10. Ok and return. + // Ok and return. Ok(()) } @@ -147,59 +100,19 @@ impl<T: Config> Pallet<T> { destination_netuid: u16, alpha_amount: u64, ) -> dispatch::DispatchResult { - // 1. Ensure the extrinsic is signed by the origin_coldkey. + // Ensure the extrinsic is signed by the origin_coldkey. let coldkey = ensure_signed(origin)?; - // 2. Ensure both subnets exist. - ensure!( - Self::if_subnet_exist(origin_netuid), - Error::<T>::SubnetNotExists - ); - ensure!( - Self::if_subnet_exist(destination_netuid), - Error::<T>::SubnetNotExists - ); - - // 3. Check that the hotkey exists. - ensure!( - Self::hotkey_account_exists(&hotkey), - Error::<T>::HotKeyAccountNotExists - ); - - // 4. Check that the signed coldkey actually owns the given hotkey. - ensure!( - Self::coldkey_owns_hotkey(&coldkey, &hotkey), - Error::<T>::NonAssociatedColdKey - ); - - // 5. Get current stake. - let origin_alpha = - Self::get_stake_for_hotkey_and_coldkey_on_subnet(&hotkey, &coldkey, origin_netuid); - ensure!( - alpha_amount <= origin_alpha, - Error::<T>::NotEnoughStakeToWithdraw - ); - - // 6. Unstake from the origin coldkey; this returns an amount of TAO. - let fee = DefaultStakingFee::<T>::get().saturating_div(2); - let origin_tao = - Self::unstake_from_subnet(&hotkey, &coldkey, origin_netuid, alpha_amount, fee); - - // 7. Ensure the returned TAO meets a minimum stake requirement (if required). - ensure!( - origin_tao.saturating_sub(fee) >= DefaultMinStake::<T>::get(), - Error::<T>::AmountTooLow - ); - - // 8. Stake the TAO into `(destination_coldkey, hotkey)` on the destination subnet. - // Create the account if it does not exist. - Self::stake_into_subnet( - &hotkey, + // Validate input and move stake + let tao_moved = Self::transition_stake_internal( + &coldkey, &destination_coldkey, + &hotkey, + &hotkey, + origin_netuid, destination_netuid, - origin_tao, - fee, - ); + alpha_amount, + )?; // 9. Emit an event for logging/monitoring. log::info!( @@ -209,7 +122,7 @@ impl<T: Config> Pallet<T> { hotkey, origin_netuid, destination_netuid, - origin_tao + tao_moved ); Self::deposit_event(Event::StakeTransferred( coldkey, @@ -217,7 +130,7 @@ impl<T: Config> Pallet<T> { hotkey, origin_netuid, destination_netuid, - origin_tao, + tao_moved, )); // 10. Return success. @@ -254,71 +167,84 @@ impl<T: Config> Pallet<T> { destination_netuid: u16, alpha_amount: u64, ) -> dispatch::DispatchResult { - // 1. Ensure the extrinsic is signed by the coldkey. + // Ensure the extrinsic is signed by the coldkey. let coldkey = ensure_signed(origin)?; - // 2. Check that both subnets exist. - ensure!( - Self::if_subnet_exist(origin_netuid), - Error::<T>::SubnetNotExists - ); - ensure!( - Self::if_subnet_exist(destination_netuid), - Error::<T>::SubnetNotExists - ); - - // 3. Check that the hotkey exists. - ensure!( - Self::hotkey_account_exists(&hotkey), - Error::<T>::HotKeyAccountNotExists - ); - - // 4. Ensure this coldkey actually owns the hotkey. - ensure!( - Self::coldkey_owns_hotkey(&coldkey, &hotkey), - Error::<T>::NonAssociatedColdKey - ); - - // 5. Ensure there is enough stake in the origin subnet. - let origin_alpha = - Self::get_stake_for_hotkey_and_coldkey_on_subnet(&hotkey, &coldkey, origin_netuid); - ensure!( - alpha_amount <= origin_alpha, - Error::<T>::NotEnoughStakeToWithdraw - ); - - // 6. Unstake from the origin subnet, returning TAO (or a 1:1 equivalent). - let fee = DefaultStakingFee::<T>::get().saturating_div(2); - let tao_unstaked = - Self::unstake_from_subnet(&hotkey, &coldkey, origin_netuid, alpha_amount, fee); - - // 7. Check that the unstaked amount is above the minimum stake threshold. - ensure!( - tao_unstaked.saturating_sub(fee) >= DefaultMinStake::<T>::get(), - Error::<T>::AmountTooLow - ); - - // 8. Stake the unstaked amount into the destination subnet, using the same coldkey/hotkey. - Self::stake_into_subnet(&hotkey, &coldkey, destination_netuid, tao_unstaked, fee); + // Validate input and move stake + let tao_moved = Self::transition_stake_internal( + &coldkey, + &coldkey, + &hotkey, + &hotkey, + origin_netuid, + destination_netuid, + alpha_amount, + )?; - // 9. Emit an event for logging. + // Emit an event for logging. log::info!( "StakeSwapped(coldkey: {:?}, hotkey: {:?}, origin_netuid: {:?}, destination_netuid: {:?}, amount: {:?})", coldkey, hotkey, origin_netuid, destination_netuid, - tao_unstaked + tao_moved ); Self::deposit_event(Event::StakeSwapped( coldkey, hotkey, origin_netuid, destination_netuid, - tao_unstaked, + tao_moved, )); - // 10. Return success. + // 6. Return success. Ok(()) } + + fn transition_stake_internal( + origin_coldkey: &T::AccountId, + destination_coldkey: &T::AccountId, + origin_hotkey: &T::AccountId, + destination_hotkey: &T::AccountId, + origin_netuid: u16, + destination_netuid: u16, + alpha_amount: u64, + ) -> Result<u64, Error<T>> { + // Validate user input + Self::validate_stake_transition( + origin_coldkey, + destination_coldkey, + origin_hotkey, + destination_hotkey, + origin_netuid, + destination_netuid, + alpha_amount, + )?; + + // Unstake from the origin subnet, returning TAO (or a 1:1 equivalent). + let fee = DefaultStakingFee::<T>::get().saturating_div(2); + let tao_unstaked = Self::unstake_from_subnet( + origin_hotkey, + origin_coldkey, + origin_netuid, + alpha_amount, + fee, + ); + + // Stake the unstaked amount into the destination. + // Because of the fee, the tao_unstaked may be too low if initial stake is low. In that case, + // do not restake. + if tao_unstaked >= DefaultMinStake::<T>::get().saturating_add(fee) { + Self::stake_into_subnet( + destination_hotkey, + destination_coldkey, + destination_netuid, + tao_unstaked, + fee, + ); + } + + Ok(tao_unstaked.saturating_sub(fee)) + } } diff --git a/pallets/subtensor/src/staking/remove_stake.rs b/pallets/subtensor/src/staking/remove_stake.rs index fd6fe8191..bab8c9a29 100644 --- a/pallets/subtensor/src/staking/remove_stake.rs +++ b/pallets/subtensor/src/staking/remove_stake.rs @@ -47,36 +47,21 @@ impl<T: Config> Pallet<T> { alpha_unstaked ); - // 2. Ensure that the stake amount to be removed is above zero. - ensure!(alpha_unstaked > 0, Error::<T>::StakeToWithdrawIsZero); + // 2. Validate the user input + Self::validate_remove_stake(&coldkey, &hotkey, netuid, alpha_unstaked)?; - // 3. Ensure that the subnet exists. - ensure!(Self::if_subnet_exist(netuid), Error::<T>::SubnetNotExists); - - // 4. Ensure that the hotkey account exists this is only possible through registration. - ensure!( - Self::hotkey_account_exists(&hotkey), - Error::<T>::HotKeyAccountNotExists - ); - - // 5. Ensure that the hotkey has enough stake to withdraw. - ensure!( - Self::has_enough_stake_on_subnet(&hotkey, &coldkey, netuid, alpha_unstaked), - Error::<T>::NotEnoughStakeToWithdraw - ); - - // 6. Swap the alpba to tao and update counters for this subnet. + // 3. Swap the alpba to tao and update counters for this subnet. let fee = DefaultStakingFee::<T>::get(); let tao_unstaked: u64 = Self::unstake_from_subnet(&hotkey, &coldkey, netuid, alpha_unstaked, fee); - // 7. We add the balance to the coldkey. If the above fails we will not credit this coldkey. + // 4. We add the balance to the coldkey. If the above fails we will not credit this coldkey. Self::add_balance_to_coldkey_account(&coldkey, tao_unstaked); - // 8. If the stake is below the minimum, we clear the nomination from storage. + // 5. If the stake is below the minimum, we clear the nomination from storage. Self::clear_small_nomination_if_required(&hotkey, &coldkey, netuid); - // 9. Check if stake lowered below MinStake and remove Pending children if it did + // 6. Check if stake lowered below MinStake and remove Pending children if it did if Self::get_total_stake_for_hotkey(&hotkey) < StakeThreshold::<T>::get() { Self::get_all_subnet_netuids().iter().for_each(|netuid| { PendingChildKeys::<T>::remove(netuid, &hotkey); diff --git a/pallets/subtensor/src/staking/stake_utils.rs b/pallets/subtensor/src/staking/stake_utils.rs index d6901eec2..f34a5ace0 100644 --- a/pallets/subtensor/src/staking/stake_utils.rs +++ b/pallets/subtensor/src/staking/stake_utils.rs @@ -719,6 +719,128 @@ impl<T: Config> Pallet<T> { let ops = HotkeyAlphaSharePoolDataOperations::new(hotkey, netuid); SharePool::<AlphaShareKey<T>, HotkeyAlphaSharePoolDataOperations<T>>::new(ops) } + + /// Validate add_stake user input + /// + pub fn validate_add_stake( + coldkey: &T::AccountId, + hotkey: &T::AccountId, + netuid: u16, + stake_to_be_added: u64, + ) -> Result<(), Error<T>> { + // Ensure that the subnet exists. + ensure!(Self::if_subnet_exist(netuid), Error::<T>::SubnetNotExists); + + // Get the minimum balance (and amount) that satisfies the transaction + let min_amount = DefaultMinStake::<T>::get().saturating_add(DefaultStakingFee::<T>::get()); + + // Ensure that the stake_to_be_added is at least the min_amount + ensure!(stake_to_be_added >= min_amount, Error::<T>::AmountTooLow); + + // Ensure the callers coldkey has enough stake to perform the transaction. + ensure!( + Self::can_remove_balance_from_coldkey_account(coldkey, stake_to_be_added), + Error::<T>::NotEnoughBalanceToStake + ); + + // Ensure that the hotkey account exists this is only possible through registration. + ensure!( + Self::hotkey_account_exists(hotkey), + Error::<T>::HotKeyAccountNotExists + ); + + Ok(()) + } + + /// Validate remove_stake user input + /// + pub fn validate_remove_stake( + coldkey: &T::AccountId, + hotkey: &T::AccountId, + netuid: u16, + alpha_unstaked: u64, + ) -> Result<(), Error<T>> { + // Ensure that the subnet exists. + ensure!(Self::if_subnet_exist(netuid), Error::<T>::SubnetNotExists); + + // Ensure that the stake amount to be removed is above the minimum in tao equivalent. + let tao_equivalent = Self::sim_swap_alpha_for_tao(netuid, alpha_unstaked); + ensure!( + tao_equivalent > DefaultMinStake::<T>::get(), + Error::<T>::AmountTooLow + ); + + // Ensure that the hotkey account exists this is only possible through registration. + ensure!( + Self::hotkey_account_exists(hotkey), + Error::<T>::HotKeyAccountNotExists + ); + + // Ensure that the hotkey has enough stake to withdraw. + ensure!( + Self::has_enough_stake_on_subnet(hotkey, coldkey, netuid, alpha_unstaked), + Error::<T>::NotEnoughStakeToWithdraw + ); + + Ok(()) + } + + /// Validate stake transition user input + /// That works for move_stake, transfer_stake, and swap_stake + /// + pub fn validate_stake_transition( + origin_coldkey: &T::AccountId, + _destination_coldkey: &T::AccountId, + origin_hotkey: &T::AccountId, + _destination_hotkey: &T::AccountId, + origin_netuid: u16, + destination_netuid: u16, + alpha_amount: u64, + ) -> Result<(), Error<T>> { + // Ensure that both subnets exist. + ensure!( + Self::if_subnet_exist(origin_netuid), + Error::<T>::SubnetNotExists + ); + if origin_netuid != destination_netuid { + ensure!( + Self::if_subnet_exist(destination_netuid), + Error::<T>::SubnetNotExists + ); + } + + // Ensure that the origin hotkey account exists + ensure!( + Self::hotkey_account_exists(origin_hotkey), + Error::<T>::HotKeyAccountNotExists + ); + + // Ensure origin coldkey owns the origin hotkey. + ensure!( + Self::coldkey_owns_hotkey(origin_coldkey, origin_hotkey), + Error::<T>::NonAssociatedColdKey + ); + + // Ensure there is enough stake in the origin subnet. + let origin_alpha = Self::get_stake_for_hotkey_and_coldkey_on_subnet( + origin_hotkey, + origin_coldkey, + origin_netuid, + ); + ensure!( + alpha_amount <= origin_alpha, + Error::<T>::NotEnoughStakeToWithdraw + ); + + // Ensure that the stake amount to be removed is above the minimum in tao equivalent. + let tao_equivalent = Self::sim_swap_alpha_for_tao(origin_netuid, alpha_amount); + ensure!( + tao_equivalent > DefaultMinStake::<T>::get(), + Error::<T>::AmountTooLow + ); + + Ok(()) + } } /////////////////////////////////////////// diff --git a/pallets/subtensor/src/tests/move_stake.rs b/pallets/subtensor/src/tests/move_stake.rs index 4061f07e7..6af41b388 100644 --- a/pallets/subtensor/src/tests/move_stake.rs +++ b/pallets/subtensor/src/tests/move_stake.rs @@ -511,7 +511,7 @@ fn test_do_move_wrong_origin() { let origin_hotkey = U256::from(2); let destination_hotkey = U256::from(3); let netuid = 1; - let stake_amount = 1000; + let stake_amount = DefaultMinStake::<Test>::get() * 10; let fee = 0; // Set up initial stake @@ -535,7 +535,7 @@ fn test_do_move_wrong_origin() { netuid, alpha, ), - Error::<Test>::NotEnoughStakeToWithdraw + Error::<Test>::NonAssociatedColdKey ); // Check that no stake was moved @@ -770,7 +770,7 @@ fn test_do_move_max_values() { // Verify moving too low amount is impossible #[test] -fn test_moving_too_little_fails() { +fn test_moving_too_little_unstakes() { new_test_ext(1).execute_with(|| { let hotkey_account_id = U256::from(533453); let coldkey_account_id = U256::from(55453); @@ -791,7 +791,6 @@ fn test_moving_too_little_fails() { amount + fee )); - // 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), diff --git a/pallets/subtensor/src/tests/registration.rs b/pallets/subtensor/src/tests/registration.rs index 07a456c83..e4d1b1045 100644 --- a/pallets/subtensor/src/tests/registration.rs +++ b/pallets/subtensor/src/tests/registration.rs @@ -3,7 +3,7 @@ use frame_support::traits::Currency; use super::mock::*; -use crate::{AxonInfoOf, Error, SubtensorSignedExtension}; +use crate::{AxonInfoOf, CustomTransactionError, Error, SubtensorSignedExtension}; use frame_support::dispatch::{DispatchClass, DispatchInfo, GetDispatchInfo, Pays}; use frame_support::sp_runtime::{transaction_validity::InvalidTransaction, DispatchError}; use frame_support::{assert_err, assert_noop, assert_ok}; @@ -274,7 +274,10 @@ fn test_registration_rate_limit_exceeded() { let result = extension.validate(&who, &call.into(), &info, 10); // Expectation: The transaction should be rejected - assert_err!(result, InvalidTransaction::Custom(5)); + assert_err!( + result, + InvalidTransaction::Custom(CustomTransactionError::RateLimitExceeded.into()) + ); let current_registrants = SubtensorModule::get_registrations_this_interval(netuid); assert!(current_registrants <= max_registrants); @@ -356,7 +359,10 @@ fn test_burned_registration_rate_limit_exceeded() { extension.validate(&who, &call_burned_register.into(), &info, 10); // Expectation: The transaction should be rejected - assert_err!(burned_register_result, InvalidTransaction::Custom(5)); + assert_err!( + burned_register_result, + InvalidTransaction::Custom(CustomTransactionError::RateLimitExceeded.into()) + ); let current_registrants = SubtensorModule::get_registrations_this_interval(netuid); assert!(current_registrants <= max_registrants); diff --git a/pallets/subtensor/src/tests/staking.rs b/pallets/subtensor/src/tests/staking.rs index f57fb4ebe..8087e85df 100644 --- a/pallets/subtensor/src/tests/staking.rs +++ b/pallets/subtensor/src/tests/staking.rs @@ -19,12 +19,12 @@ use sp_core::{Get, H256, U256}; fn test_add_stake_dispatch_info_ok() { new_test_ext(1).execute_with(|| { let hotkey = U256::from(0); - let amount_staked = 5000; + let amount = 5000; let netuid = 1; let call = RuntimeCall::SubtensorModule(SubtensorCall::add_stake { hotkey, netuid, - amount_staked, + amount, }); assert_eq!( call.get_dispatch_info(), @@ -158,8 +158,9 @@ fn test_add_stake_not_registered_key_pair() { let coldkey_account_id = U256::from(435445); let hotkey_account_id = U256::from(54544); let netuid = add_dynamic_network(&subnet_owner_hotkey, &subnet_owner_coldkey); - let amount = 1337; - SubtensorModule::add_balance_to_coldkey_account(&coldkey_account_id, 1800); + let amount = DefaultMinStake::<Test>::get() * 10; + let fee = DefaultStakingFee::<Test>::get(); + SubtensorModule::add_balance_to_coldkey_account(&coldkey_account_id, amount + fee); assert_err!( SubtensorModule::add_stake( RuntimeOrigin::signed(coldkey_account_id), @@ -390,7 +391,7 @@ fn test_remove_stake_ok_no_emission() { } #[test] -fn test_remove_stake_amount_zero() { +fn test_remove_stake_amount_too_low() { new_test_ext(1).execute_with(|| { let subnet_owner_coldkey = U256::from(1); let subnet_owner_hotkey = U256::from(2); @@ -424,7 +425,7 @@ fn test_remove_stake_amount_zero() { netuid, 0 ), - Error::<Test>::StakeToWithdrawIsZero + Error::<Test>::AmountTooLow ); }); } @@ -454,7 +455,7 @@ fn test_remove_stake_ok_hotkey_does_not_belong_to_coldkey() { let coldkey_id = U256::from(544); let hotkey_id = U256::from(54544); let other_cold_key = U256::from(99498); - let amount = 1000; + let amount = DefaultMinStake::<Test>::get() * 10; let netuid: u16 = add_dynamic_network(&hotkey_id, &coldkey_id); // Give the neuron some stake to remove @@ -479,7 +480,7 @@ fn test_remove_stake_no_enough_stake() { new_test_ext(1).execute_with(|| { let coldkey_id = U256::from(544); let hotkey_id = U256::from(54544); - let amount = 10000; + let amount = DefaultMinStake::<Test>::get() * 10; let netuid = add_dynamic_network(&hotkey_id, &coldkey_id); assert_eq!(SubtensorModule::get_total_stake_for_hotkey(&hotkey_id), 0); @@ -2074,17 +2075,17 @@ fn test_stake_below_min_validate() { let subnet_owner_hotkey = U256::from(1002); let hotkey = U256::from(2); let coldkey = U256::from(3); - let amount_staked = DefaultMinStake::<Test>::get() + DefaultStakingFee::<Test>::get() - 1; + let amount = DefaultMinStake::<Test>::get() + DefaultStakingFee::<Test>::get() - 1; let netuid = add_dynamic_network(&subnet_owner_hotkey, &subnet_owner_coldkey); SubtensorModule::create_account_if_non_existent(&coldkey, &hotkey); - SubtensorModule::add_balance_to_coldkey_account(&coldkey, amount_staked); + SubtensorModule::add_balance_to_coldkey_account(&coldkey, amount); // Add stake call let call = RuntimeCall::SubtensorModule(SubtensorCall::add_stake { hotkey, netuid, - amount_staked, + amount, }); let info: crate::DispatchInfo = @@ -2097,24 +2098,28 @@ fn test_stake_below_min_validate() { // Should fail due to insufficient stake assert_err!( result_no_stake, - crate::TransactionValidityError::Invalid(crate::InvalidTransaction::Custom(1)) + crate::TransactionValidityError::Invalid(crate::InvalidTransaction::Custom( + CustomTransactionError::StakeAmountTooLow.into() + )) ); // Increase the stake to be equal to the minimum, but leave the balance low - let amount_staked = DefaultMinStake::<Test>::get() + DefaultStakingFee::<Test>::get(); + let amount = DefaultMinStake::<Test>::get() + DefaultStakingFee::<Test>::get(); let call_2 = RuntimeCall::SubtensorModule(SubtensorCall::add_stake { hotkey, netuid, - amount_staked, + amount, }); // Submit to the signed extension validate function let result_low_balance = extension.validate(&coldkey, &call_2.clone(), &info, 10); - // Still doesn't pass + // Still doesn't pass, but with a different reason (balance too low) assert_err!( result_low_balance, - crate::TransactionValidityError::Invalid(crate::InvalidTransaction::Custom(2)) + crate::TransactionValidityError::Invalid(crate::InvalidTransaction::Custom( + CustomTransactionError::BalanceTooLow.into() + )) ); // Increase the coldkey balance to match the minimum diff --git a/pallets/subtensor/src/tests/weights.rs b/pallets/subtensor/src/tests/weights.rs index 9a89ec76e..3b6919064 100644 --- a/pallets/subtensor/src/tests/weights.rs +++ b/pallets/subtensor/src/tests/weights.rs @@ -105,7 +105,9 @@ fn test_set_rootweights_validate() { assert_err!( // Should get an invalid transaction error result_no_stake, - crate::TransactionValidityError::Invalid(crate::InvalidTransaction::Custom(4)) + crate::TransactionValidityError::Invalid(crate::InvalidTransaction::Custom( + CustomTransactionError::StakeAmountTooLow.into() + )) ); // Increase the stake to be equal to the minimum @@ -328,7 +330,9 @@ fn test_set_weights_validate() { // Should fail due to insufficient stake assert_err!( result_no_stake, - crate::TransactionValidityError::Invalid(crate::InvalidTransaction::Custom(3)) + crate::TransactionValidityError::Invalid(crate::InvalidTransaction::Custom( + CustomTransactionError::StakeAmountTooLow.into() + )) ); // Increase the stake to be equal to the minimum @@ -402,7 +406,9 @@ fn test_reveal_weights_validate() { assert_err!( // Should get an invalid transaction error result_no_stake, - crate::TransactionValidityError::Invalid(crate::InvalidTransaction::Custom(2)) + crate::TransactionValidityError::Invalid(crate::InvalidTransaction::Custom( + CustomTransactionError::StakeAmountTooLow.into() + )) ); // Increase the stake to be equal to the minimum diff --git a/runtime/src/precompiles/staking.rs b/runtime/src/precompiles/staking.rs index 86d257a19..743000dec 100644 --- a/runtime/src/precompiles/staking.rs +++ b/runtime/src/precompiles/staking.rs @@ -85,7 +85,7 @@ impl StakingPrecompile { let call = RuntimeCall::SubtensorModule(pallet_subtensor::Call::<Runtime>::add_stake { hotkey, netuid, - amount_staked: amount_sub.unique_saturated_into(), + amount: amount_sub.unique_saturated_into(), }); // Dispatch the add_stake call Self::dispatch(handle, call) diff --git a/runtime/tests/pallet_proxy.rs b/runtime/tests/pallet_proxy.rs index 2cfbd908b..1aad63283 100644 --- a/runtime/tests/pallet_proxy.rs +++ b/runtime/tests/pallet_proxy.rs @@ -103,11 +103,11 @@ fn call_senate() -> RuntimeCall { // staking call fn call_add_stake() -> RuntimeCall { let netuid = 1; - let amount_staked = 100; + let amount = 100; RuntimeCall::SubtensorModule(pallet_subtensor::Call::add_stake { hotkey: AccountId::from(DELEGATE), netuid, - amount_staked, + amount, }) } From f0534f1cb9fb96c601771ba31bc12b3850aa09dd Mon Sep 17 00:00:00 2001 From: Greg Zaitsev <gregz@opentensor.dev> Date: Thu, 23 Jan 2025 18:57:30 -0500 Subject: [PATCH 062/145] Fix merge conflicts --- pallets/subtensor/src/tests/move_stake.rs | 18 ++++-------------- 1 file changed, 4 insertions(+), 14 deletions(-) diff --git a/pallets/subtensor/src/tests/move_stake.rs b/pallets/subtensor/src/tests/move_stake.rs index 11939a1a6..c7f88f985 100644 --- a/pallets/subtensor/src/tests/move_stake.rs +++ b/pallets/subtensor/src/tests/move_stake.rs @@ -1079,8 +1079,6 @@ fn test_do_swap_success() { 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 fee = DefaultMinStake::<Test>::get(); - let coldkey = U256::from(1); let hotkey = U256::from(2); let stake_amount = DefaultMinStake::<Test>::get() * 10; @@ -1265,8 +1263,6 @@ fn test_do_swap_same_subnet() { let subnet_owner_hotkey = U256::from(1101); let netuid = add_dynamic_network(&subnet_owner_hotkey, &subnet_owner_coldkey); - let fee = DefaultMinStake::<Test>::get(); - let coldkey = U256::from(1); let hotkey = U256::from(2); let stake_amount = DefaultMinStake::<Test>::get() * 10; @@ -1291,8 +1287,8 @@ fn test_do_swap_same_subnet() { SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet(&hotkey, &coldkey, netuid); assert_abs_diff_eq!( alpha_after, - alpha_before - fee, - epsilon = alpha_after / 1000 + alpha_before - fee_as_alpha, + epsilon = alpha_after / 10000 ); }); } @@ -1305,8 +1301,6 @@ fn test_do_swap_partial_stake() { 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 fee = DefaultMinStake::<Test>::get(); - let coldkey = U256::from(1); let hotkey = U256::from(2); let total_stake = DefaultMinStake::<Test>::get() * 10; @@ -1354,8 +1348,6 @@ fn test_do_swap_storage_updates() { 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 fee = DefaultMinStake::<Test>::get(); - let coldkey = U256::from(1); let hotkey = U256::from(2); let stake_amount = DefaultMinStake::<Test>::get() * 10; @@ -1408,8 +1400,6 @@ fn test_do_swap_multiple_times() { let netuid1 = add_dynamic_network(&subnet_owner_hotkey, &subnet_owner_coldkey); let netuid2 = add_dynamic_network(&subnet_owner_hotkey, &subnet_owner_coldkey); - let fee = DefaultMinStake::<Test>::get(); - let coldkey = U256::from(1); let hotkey = U256::from(2); let initial_stake = DefaultMinStake::<Test>::get() * 10; @@ -1459,8 +1449,8 @@ fn test_do_swap_multiple_times() { let expected_stake = initial_stake - total_alpha1_fee; assert_abs_diff_eq!( final_stake_netuid1, - initial_stake - 6 * fee, - epsilon = initial_stake / 1000 + expected_stake, + epsilon = initial_stake / 10000 ); assert_eq!(final_stake_netuid2, 0); }); From ab4d6e1bccb519c270850b19b35b442113ee8b1f Mon Sep 17 00:00:00 2001 From: Greg Zaitsev <gregz@opentensor.dev> Date: Thu, 23 Jan 2025 19:09:03 -0500 Subject: [PATCH 063/145] Rename amount back to amount_staked in add_stake parameters --- pallets/subtensor/src/lib.rs | 4 ++-- pallets/subtensor/src/macros/dispatches.rs | 4 ++-- pallets/subtensor/src/tests/staking.rs | 14 +++++++------- runtime/src/precompiles/staking.rs | 2 +- runtime/tests/pallet_proxy.rs | 4 ++-- 5 files changed, 14 insertions(+), 14 deletions(-) diff --git a/pallets/subtensor/src/lib.rs b/pallets/subtensor/src/lib.rs index 2a7c32054..45012625c 100644 --- a/pallets/subtensor/src/lib.rs +++ b/pallets/subtensor/src/lib.rs @@ -1781,11 +1781,11 @@ where Some(Call::add_stake { hotkey, netuid, - amount, + amount_staked, }) => { // Fully validate the user input Self::result_to_validity(Pallet::<T>::validate_add_stake( - who, hotkey, *netuid, *amount, + who, hotkey, *netuid, *amount_staked, )) } Some(Call::remove_stake { diff --git a/pallets/subtensor/src/macros/dispatches.rs b/pallets/subtensor/src/macros/dispatches.rs index 2f4e2f563..d9eafffa7 100644 --- a/pallets/subtensor/src/macros/dispatches.rs +++ b/pallets/subtensor/src/macros/dispatches.rs @@ -584,9 +584,9 @@ mod dispatches { origin: OriginFor<T>, hotkey: T::AccountId, netuid: u16, - amount: u64, + amount_staked: u64, ) -> DispatchResult { - Self::do_add_stake(origin, hotkey, netuid, amount) + Self::do_add_stake(origin, hotkey, netuid, amount_staked) } /// Remove stake from the staking account. The call must be made diff --git a/pallets/subtensor/src/tests/staking.rs b/pallets/subtensor/src/tests/staking.rs index 8087e85df..f73f9b447 100644 --- a/pallets/subtensor/src/tests/staking.rs +++ b/pallets/subtensor/src/tests/staking.rs @@ -19,12 +19,12 @@ use sp_core::{Get, H256, U256}; fn test_add_stake_dispatch_info_ok() { new_test_ext(1).execute_with(|| { let hotkey = U256::from(0); - let amount = 5000; + let amount_staked = 5000; let netuid = 1; let call = RuntimeCall::SubtensorModule(SubtensorCall::add_stake { hotkey, netuid, - amount, + amount_staked, }); assert_eq!( call.get_dispatch_info(), @@ -2075,17 +2075,17 @@ fn test_stake_below_min_validate() { let subnet_owner_hotkey = U256::from(1002); let hotkey = U256::from(2); let coldkey = U256::from(3); - let amount = DefaultMinStake::<Test>::get() + DefaultStakingFee::<Test>::get() - 1; + let amount_staked = DefaultMinStake::<Test>::get() + DefaultStakingFee::<Test>::get() - 1; let netuid = add_dynamic_network(&subnet_owner_hotkey, &subnet_owner_coldkey); SubtensorModule::create_account_if_non_existent(&coldkey, &hotkey); - SubtensorModule::add_balance_to_coldkey_account(&coldkey, amount); + SubtensorModule::add_balance_to_coldkey_account(&coldkey, amount_staked); // Add stake call let call = RuntimeCall::SubtensorModule(SubtensorCall::add_stake { hotkey, netuid, - amount, + amount_staked, }); let info: crate::DispatchInfo = @@ -2104,11 +2104,11 @@ fn test_stake_below_min_validate() { ); // Increase the stake to be equal to the minimum, but leave the balance low - let amount = DefaultMinStake::<Test>::get() + DefaultStakingFee::<Test>::get(); + let amount_staked = DefaultMinStake::<Test>::get() + DefaultStakingFee::<Test>::get(); let call_2 = RuntimeCall::SubtensorModule(SubtensorCall::add_stake { hotkey, netuid, - amount, + amount_staked, }); // Submit to the signed extension validate function diff --git a/runtime/src/precompiles/staking.rs b/runtime/src/precompiles/staking.rs index 169802dc4..f8534927b 100644 --- a/runtime/src/precompiles/staking.rs +++ b/runtime/src/precompiles/staking.rs @@ -84,7 +84,7 @@ impl StakingPrecompile { let call = RuntimeCall::SubtensorModule(pallet_subtensor::Call::<Runtime>::add_stake { hotkey, netuid, - amount: amount_sub.unique_saturated_into(), + amount_staked: amount_sub.unique_saturated_into(), }); // Dispatch the add_stake call Self::dispatch(handle, call) diff --git a/runtime/tests/pallet_proxy.rs b/runtime/tests/pallet_proxy.rs index 1aad63283..2cfbd908b 100644 --- a/runtime/tests/pallet_proxy.rs +++ b/runtime/tests/pallet_proxy.rs @@ -103,11 +103,11 @@ fn call_senate() -> RuntimeCall { // staking call fn call_add_stake() -> RuntimeCall { let netuid = 1; - let amount = 100; + let amount_staked = 100; RuntimeCall::SubtensorModule(pallet_subtensor::Call::add_stake { hotkey: AccountId::from(DELEGATE), netuid, - amount, + amount_staked, }) } From beb6f6b144ddebc39f06ce72c98cd2a43dc427cd Mon Sep 17 00:00:00 2001 From: Greg Zaitsev <gregz@opentensor.dev> Date: Fri, 24 Jan 2025 09:11:09 -0500 Subject: [PATCH 064/145] Format and bump spec --- pallets/subtensor/src/lib.rs | 5 ++++- runtime/src/lib.rs | 2 +- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/pallets/subtensor/src/lib.rs b/pallets/subtensor/src/lib.rs index 45012625c..cbe394037 100644 --- a/pallets/subtensor/src/lib.rs +++ b/pallets/subtensor/src/lib.rs @@ -1785,7 +1785,10 @@ where }) => { // Fully validate the user input Self::result_to_validity(Pallet::<T>::validate_add_stake( - who, hotkey, *netuid, *amount_staked, + who, + hotkey, + *netuid, + *amount_staked, )) } Some(Call::remove_stake { diff --git a/runtime/src/lib.rs b/runtime/src/lib.rs index e0ea63987..f8491dd57 100644 --- a/runtime/src/lib.rs +++ b/runtime/src/lib.rs @@ -220,7 +220,7 @@ pub const VERSION: RuntimeVersion = RuntimeVersion { // `spec_version`, and `authoring_version` are the same between Wasm and native. // This value is set to 100 to notify Polkadot-JS App (https://polkadot.js.org/apps) to use // the compatible custom types. - spec_version: 223, + spec_version: 224, impl_version: 1, apis: RUNTIME_API_VERSIONS, transaction_version: 1, From d91dc3c8e0f7af7916543244195187db1429a9cc Mon Sep 17 00:00:00 2001 From: Cameron Fairchild <cameron@opentensor.ai> Date: Fri, 24 Jan 2025 10:09:06 -0500 Subject: [PATCH 065/145] Fix/run set pending children on epoch (#1171) * add back running set-pending-children * add test * move to step block * commit Cargo.lock * use mock sched children at end of loop instead * set reg diff before try reg again * oops, add netuid * fix tests using add network tempo 0 -> 1 * chore: fmt * only wait blocks after schedule all children for tests * fix test: wip * charge fee for swap and add for tests * wip * chore: clippy * chore: fmt * use get_dynamic_tao_emission for testing emission * spec bump --- pallets/subtensor/src/coinbase/block_step.rs | 14 ++ pallets/subtensor/src/tests/children.rs | 191 +++++++++++++++---- pallets/subtensor/src/tests/epoch.rs | 2 +- pallets/subtensor/src/tests/mock.rs | 22 ++- pallets/subtensor/src/tests/move_stake.rs | 4 +- pallets/subtensor/src/tests/registration.rs | 4 +- pallets/subtensor/src/tests/senate.rs | 2 +- pallets/subtensor/src/tests/staking.rs | 18 +- pallets/subtensor/src/tests/swap_hotkey.rs | 26 +-- pallets/subtensor/src/tests/weights.rs | 16 +- runtime/src/lib.rs | 2 +- 11 files changed, 225 insertions(+), 76 deletions(-) diff --git a/pallets/subtensor/src/coinbase/block_step.rs b/pallets/subtensor/src/coinbase/block_step.rs index c3abf8dab..23218ed50 100644 --- a/pallets/subtensor/src/coinbase/block_step.rs +++ b/pallets/subtensor/src/coinbase/block_step.rs @@ -14,10 +14,24 @@ impl<T: Config + pallet_drand::Config> Pallet<T> { log::debug!("Block emission: {:?}", block_emission); // --- 3. Run emission through network. Self::run_coinbase(block_emission); + + // --- 4. Set pending children on the epoch; but only after the coinbase has been run. + Self::try_set_pending_children(block_number); + // Return ok. Ok(()) } + fn try_set_pending_children(block_number: u64) { + let subnets: Vec<u16> = Self::get_all_subnet_netuids(); + for &netuid in subnets.iter() { + if Self::should_run_epoch(netuid, block_number) { + // Set pending children on the epoch. + Self::do_set_pending_children(netuid); + } + } + } + /// Adjusts the network difficulties/burns of every active network. Resetting state parameters. /// pub fn adjust_registration_terms_for_networks() { diff --git a/pallets/subtensor/src/tests/children.rs b/pallets/subtensor/src/tests/children.rs index eb299d88e..30fe8287b 100644 --- a/pallets/subtensor/src/tests/children.rs +++ b/pallets/subtensor/src/tests/children.rs @@ -331,7 +331,7 @@ fn test_add_singular_child() { ), Err(Error::<Test>::SubNetworkDoesNotExist.into()) ); - add_network(netuid, 0, 0); + add_network(netuid, 1, 0); step_rate_limit(&TransactionType::SetChildren, netuid); assert_eq!( SubtensorModule::do_schedule_children( @@ -376,7 +376,7 @@ fn test_get_stake_for_hotkey_on_subnet() { let child = U256::from(2); let coldkey1 = U256::from(3); let coldkey2 = U256::from(4); - add_network(netuid, 0, 0); + add_network(netuid, 1, 0); register_ok_neuron(netuid, parent, coldkey1, 0); register_ok_neuron(netuid, child, coldkey2, 0); // Set parent-child relationship with 100% stake allocation @@ -1664,7 +1664,7 @@ fn test_get_parents_chain() { // Set up parent-child relationships for i in 0..num_keys - 1 { - mock_set_children( + mock_schedule_children( &coldkey, &hotkeys[i], netuid, @@ -1677,6 +1677,8 @@ fn test_get_parents_chain() { proportion ); } + // Wait for children to be set + wait_and_set_pending_children(netuid); // Test get_parents for each hotkey for i in 1..num_keys { @@ -1715,7 +1717,9 @@ fn test_get_parents_chain() { // Test multiple parents let last_hotkey = hotkeys[num_keys - 1]; let new_parent = U256::from(num_keys as u64 + 2); - register_ok_neuron(netuid, new_parent, coldkey, 0); + // Set reg diff back down (adjusted from last block steps) + SubtensorModule::set_difficulty(netuid, 1); + register_ok_neuron(netuid, new_parent, coldkey, 99 * 2); log::info!( "Registered new parent neuron: new_parent={}, coldkey={}, netuid={}", new_parent, @@ -1772,7 +1776,7 @@ fn test_get_stake_for_hotkey_on_subnet_basic() { let hotkey = U256::from(1); let coldkey = U256::from(2); - add_network(netuid, 0, 0); + add_network(netuid, 1, 0); register_ok_neuron(netuid, hotkey, coldkey, 0); SubtensorModule::increase_stake_for_hotkey_and_coldkey_on_subnet( &hotkey, &coldkey, netuid, 1000, @@ -1798,7 +1802,7 @@ fn test_get_stake_for_hotkey_on_subnet_multiple_coldkeys() { let coldkey1 = U256::from(2); let coldkey2 = U256::from(3); - add_network(netuid, 0, 0); + add_network(netuid, 1, 0); register_ok_neuron(netuid, hotkey, coldkey1, 0); SubtensorModule::increase_stake_for_hotkey_and_coldkey_on_subnet( @@ -1831,7 +1835,7 @@ fn test_get_stake_for_hotkey_on_subnet_single_parent_child() { let child = U256::from(2); let coldkey = U256::from(3); - add_network(netuid, 0, 0); + add_network(netuid, 1, 0); register_ok_neuron(netuid, parent, coldkey, 0); register_ok_neuron(netuid, child, coldkey, 0); @@ -1869,7 +1873,7 @@ fn test_get_stake_for_hotkey_on_subnet_multiple_parents_single_child() { let child = U256::from(3); let coldkey = U256::from(4); - add_network(netuid, 0, 0); + add_network(netuid, 1, 0); register_ok_neuron(netuid, parent1, coldkey, 0); register_ok_neuron(netuid, parent2, coldkey, 0); register_ok_neuron(netuid, child, coldkey, 0); @@ -1922,7 +1926,7 @@ fn test_get_stake_for_hotkey_on_subnet_single_parent_multiple_children() { let child2 = U256::from(3); let coldkey = U256::from(4); - add_network(netuid, 0, 0); + add_network(netuid, 1, 0); register_ok_neuron(netuid, parent, coldkey, 0); register_ok_neuron(netuid, child1, coldkey, 0); register_ok_neuron(netuid, child2, coldkey, 0); @@ -1985,7 +1989,7 @@ fn test_get_stake_for_hotkey_on_subnet_edge_cases() { let child2 = U256::from(3); let coldkey = U256::from(4); - add_network(netuid, 0, 0); + add_network(netuid, 1, 0); register_ok_neuron(netuid, parent, coldkey, 0); register_ok_neuron(netuid, child1, coldkey, 0); register_ok_neuron(netuid, child2, coldkey, 0); @@ -2057,7 +2061,7 @@ fn test_get_stake_for_hotkey_on_subnet_complex_hierarchy() { let coldkey_child2 = U256::from(7); let coldkey_grandchild = U256::from(8); - add_network(netuid, 0, 0); + add_network(netuid, 1, 0); SubtensorModule::set_max_registrations_per_block(netuid, 1000); SubtensorModule::set_target_registrations_per_interval(netuid, 1000); register_ok_neuron(netuid, parent, coldkey_parent, 0); @@ -2247,8 +2251,8 @@ fn test_get_stake_for_hotkey_on_subnet_multiple_networks() { let hotkey = U256::from(1); let coldkey = U256::from(2); - add_network(netuid1, 0, 0); - add_network(netuid2, 0, 0); + add_network(netuid1, 1, 0); + add_network(netuid2, 1, 0); register_ok_neuron(netuid1, hotkey, coldkey, 0); register_ok_neuron(netuid2, hotkey, coldkey, 0); @@ -2439,6 +2443,73 @@ fn test_do_set_child_cooldown_period() { }); } +// Test that pending childkeys get set during the epoch after the cooldown period. +// +// SKIP_WASM_BUILD=1 RUST_LOG=debug cargo test --package pallet-subtensor --lib -- tests::children::test_do_set_pending_children_runs_in_epoch --exact --show-output --nocapture +#[cfg(test)] +#[test] +fn test_do_set_pending_children_runs_in_epoch() { + new_test_ext(1).execute_with(|| { + let coldkey = U256::from(1); + let parent = U256::from(2); + let child = U256::from(3); + let netuid: u16 = 1; + let proportion: u64 = 1000; + + // Add network and register hotkey + add_network(netuid, 13, 0); + register_ok_neuron(netuid, parent, coldkey, 0); + + // Set minimum stake for setting children + SubtensorModule::increase_stake_for_hotkey_and_coldkey_on_subnet( + &parent, + &coldkey, + netuid, + StakeThreshold::<Test>::get(), + ); + + // Schedule parent-child relationship + assert_ok!(SubtensorModule::do_schedule_children( + RuntimeOrigin::signed(coldkey), + parent, + netuid, + vec![(proportion, child)], + )); + + // Ensure the childkeys are not yet applied + let children_before = SubtensorModule::get_children(&parent, netuid); + close( + children_before.len() as u64, + 0, + 0, + "Children vector should be empty before cooldown", + ); + + wait_set_pending_children_cooldown(netuid); + + // Verify child assignment + let children_after = SubtensorModule::get_children(&parent, netuid); + close( + children_after.len() as u64, + 1, + 0, + "Children vector should have one entry after cooldown", + ); + close( + children_after[0].0, + proportion, + 0, + "Child proportion should match", + ); + close( + children_after[0].1.try_into().unwrap(), + child.try_into().unwrap(), + 0, + "Child key should match", + ); + }); +} + // Test that revoking childkeys does not require minimum stake // SKIP_WASM_BUILD=1 RUST_LOG=debug cargo test --package pallet-subtensor --lib -- tests::children::test_revoke_child_no_min_stake_check --exact --show-output --nocapture #[test] @@ -3033,14 +3104,17 @@ fn test_childkey_take_drain_validator_take() { // - Runs an epoch // - Checks the emission distribution among parents, child, and weight setter // - Verifies that all parties received emissions and the total stake increased correctly -// SKIP_WASM_BUILD=1 RUST_LOG=debug cargo test --test coinbase test_childkey_multiple_parents_emission -- --nocapture +// SKIP_WASM_BUILD=1 RUST_LOG=debug cargo test --package pallet-subtensor --lib -- tests::children::test_childkey_multiple_parents_emission --exact --nocapture #[test] fn test_childkey_multiple_parents_emission() { new_test_ext(1).execute_with(|| { let subnet_owner_coldkey = U256::from(1001); - let subnet_owner_hotkey = U256::from(1002); + let subnet_owner_hotkey: U256 = U256::from(1002); let netuid = add_dynamic_network(&subnet_owner_hotkey, &subnet_owner_coldkey); Tempo::<Test>::insert(netuid, 10); // run epoch every 10 blocks + // Set subnet owner cut to 0 + SubtensorModule::set_subnet_owner_cut(0); + SubtensorModule::set_tao_weight(0); // No TAO weight // Set registration parameters and emission tempo SubtensorModule::set_max_registrations_per_block(netuid, 1000); @@ -3064,9 +3138,9 @@ fn test_childkey_multiple_parents_emission() { (true, coldkey_weight_setter, weight_setter, 100_000_000), ]; - let initial_actual_stakes: Vec<u64> = initial_stakes + initial_stakes .iter() - .map(|(register, coldkey, hotkey, stake)| { + .for_each(|(register, coldkey, hotkey, stake)| { SubtensorModule::add_balance_to_coldkey_account(coldkey, *stake); if *register { // Register a neuron @@ -3081,20 +3155,28 @@ fn test_childkey_multiple_parents_emission() { 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); // Set parent-child relationships - mock_set_children(&coldkey_parent1, &parent1, netuid, &[(u64::MAX, child)]); - mock_set_children(&coldkey_parent2, &parent2, netuid, &[(u64::MAX / 2, child)]); + mock_schedule_children(&coldkey_parent1, &parent1, netuid, &[(u64::MAX, child)]); + mock_schedule_children(&coldkey_parent2, &parent2, netuid, &[(u64::MAX / 2, child)]); + wait_and_set_pending_children(netuid); ChildkeyTake::<Test>::insert(child, netuid, u16::MAX / 5); + // Set pending emission to 0 + PendingEmission::<Test>::insert(netuid, 0); + + let initial_actual_stakes: Vec<u64> = initial_stakes + .iter() + .map(|(_register, coldkey, hotkey, _stake)| { + // Return actual stake + SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet(hotkey, coldkey, netuid) + }) + .collect(); + // Set weights (subnet owner is uid 0, ignore him) let uids: Vec<u16> = vec![1, 2]; let values: Vec<u16> = vec![65354, 65354]; @@ -3107,7 +3189,7 @@ fn test_childkey_multiple_parents_emission() { values, version_key )); - + log::info!("Running an epoch"); // Wait until epoch let start_block = SubtensorModule::get_current_block_as_u64(); loop { @@ -3118,8 +3200,19 @@ fn test_childkey_multiple_parents_emission() { } step_block(1); } - let total_emission = SubtensorModule::get_block_emission().unwrap_or(0) - * (SubtensorModule::get_current_block_as_u64() - start_block + 1); + // We substract one because we are running it *after* the epoch, so we don't expect it to effect the emission. + let blocks_passed = SubtensorModule::get_current_block_as_u64() - start_block - 1; + log::info!("blocks_passed: {:?}", blocks_passed); + let alpha_block_emission: u64 = SubtensorModule::get_block_emission_for_issuance( + SubtensorModule::get_alpha_issuance(netuid), + ) + .unwrap_or(0); + let (_, _, per_block_emission) = SubtensorModule::get_dynamic_tao_emission( + netuid, + SubtensorModule::get_block_emission().unwrap_or(0), + alpha_block_emission, + ); + let total_emission = per_block_emission * blocks_passed; // Check emission distribution let stakes: Vec<(U256, U256, &str)> = vec![ @@ -3200,6 +3293,19 @@ fn test_childkey_multiple_parents_emission() { "Child should have received some emission" ); + let mut total_stake_on_subnet = 0; + let hks = [parent1, parent2, child, weight_setter]; + for (hk, net, alpha) in TotalHotkeyAlpha::<Test>::iter() { + if hks.contains(&hk) && net == netuid { + total_stake_on_subnet += alpha; + } else { + log::info!("hk: {:?}, net: {:?}, alpha: {:?}", hk, net, alpha); + } + } + + log::info!("total_stake_on_subnet: {:?}", total_stake_on_subnet); + log::info!("total_stake: {:?}", TotalStake::<Test>::get()); + log::info!("total_emission: {:?}", total_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; @@ -3207,7 +3313,7 @@ fn test_childkey_multiple_parents_emission() { assert_abs_diff_eq!( total_stake, initial_total_stake + total_emission, - epsilon = total_emission / 100 + epsilon = total_emission / 100, ); }); } @@ -3280,7 +3386,7 @@ fn test_parent_child_chain_emission() { let stake_b: u64 = SubtensorModule::get_total_stake_for_hotkey(&hotkey_b); let stake_c: u64 = SubtensorModule::get_total_stake_for_hotkey(&hotkey_c); - let total_stake: I96F32 = I96F32::from_num(stake_a + stake_b + stake_c); + let _total_stake: I96F32 = I96F32::from_num(stake_a + stake_b + stake_c); // Assert initial stake is correct let rel_stake_a = I96F32::from_num(stake_a) / total_tao; @@ -3296,10 +3402,18 @@ fn test_parent_child_chain_emission() { // Set parent-child relationships // A -> B (50% of A's stake) - mock_set_children(&coldkey_a, &hotkey_a, netuid, &[(u64::MAX / 2, hotkey_b)]); + mock_schedule_children(&coldkey_a, &hotkey_a, netuid, &[(u64::MAX / 2, hotkey_b)]); // B -> C (50% of B's stake) - mock_set_children(&coldkey_b, &hotkey_b, netuid, &[(u64::MAX / 2, hotkey_c)]); + mock_schedule_children(&coldkey_b, &hotkey_b, netuid, &[(u64::MAX / 2, hotkey_c)]); + wait_and_set_pending_children(netuid); // Don't want to run blocks before both children are scheduled + + // Get old stakes after children are scheduled + let stake_a_old: u64 = SubtensorModule::get_total_stake_for_hotkey(&hotkey_a); + let stake_b_old: u64 = SubtensorModule::get_total_stake_for_hotkey(&hotkey_b); + let stake_c_old: u64 = SubtensorModule::get_total_stake_for_hotkey(&hotkey_c); + + let total_stake_old: I96F32 = I96F32::from_num(stake_a_old + stake_b_old + stake_c_old); // Set CHK take rate to 1/9 let chk_take: I96F32 = I96F32::from_num(1_f64 / 9_f64); @@ -3344,6 +3458,9 @@ fn test_parent_child_chain_emission() { "C should have pending emission of 1/9 of total emission" ); + // Set pending emission to 0 + PendingEmission::<Test>::insert(netuid, 0); + // Run epoch with a hardcoded emission value SubtensorModule::run_coinbase(hardcoded_emission); @@ -3356,10 +3473,10 @@ fn test_parent_child_chain_emission() { log::info!("Stake for hotkey B: {:?}", stake_b_new); log::info!("Stake for hotkey C: {:?}", stake_c_new); - let stake_inc_a: u64 = stake_a_new - stake_a; - let stake_inc_b: u64 = stake_b_new - stake_b; - let stake_inc_c: u64 = stake_c_new - stake_c; - let total_stake_inc: I96F32 = total_stake_new - total_stake; + let stake_inc_a: u64 = stake_a_new - stake_a_old; + let stake_inc_b: u64 = stake_b_new - stake_b_old; + let stake_inc_c: u64 = stake_c_new - stake_c_old; + let total_stake_inc: I96F32 = total_stake_new - total_stake_old; log::info!("Stake increase for hotkey A: {:?}", stake_inc_a); log::info!("Stake increase for hotkey B: {:?}", stake_inc_b); log::info!("Stake increase for hotkey C: {:?}", stake_inc_c); @@ -3406,9 +3523,9 @@ fn test_parent_child_chain_emission() { let eps: I96F32 = I96F32::from_num(10_000); assert!( - (total_stake_new - (total_stake + hardcoded_emission)).abs() <= eps, + (total_stake_new - (total_stake_old + hardcoded_emission)).abs() <= eps, "Total stake should have increased by the hardcoded emission amount {:?}", - total_stake_new - (total_stake + hardcoded_emission) + total_stake_new - (total_stake_old + hardcoded_emission) ); }); } diff --git a/pallets/subtensor/src/tests/epoch.rs b/pallets/subtensor/src/tests/epoch.rs index 7973a44e1..d80c9e074 100644 --- a/pallets/subtensor/src/tests/epoch.rs +++ b/pallets/subtensor/src/tests/epoch.rs @@ -493,7 +493,7 @@ fn init_run_epochs( // new_test_ext(1).execute_with(|| { // log::info!("test_overflow:"); // let netuid: u16 = 1; -// add_network(netuid, 0, 0); +// add_network(netuid, 1, 0); // SubtensorModule::set_max_allowed_uids(netuid, 3); // SubtensorModule::increase_stake_on_coldkey_hotkey_account( // &U256::from(0), diff --git a/pallets/subtensor/src/tests/mock.rs b/pallets/subtensor/src/tests/mock.rs index 9d5f939f6..b0d7c461a 100644 --- a/pallets/subtensor/src/tests/mock.rs +++ b/pallets/subtensor/src/tests/mock.rs @@ -601,6 +601,7 @@ pub(crate) fn step_epochs(count: u16, netuid: u16) { SubtensorModule::get_tempo(netuid), SubtensorModule::get_current_block_as_u64(), ); + log::info!("Blocks to next epoch: {:?}", blocks_to_next_epoch); step_block(blocks_to_next_epoch as u16); assert!(SubtensorModule::should_run_epoch( @@ -686,16 +687,28 @@ pub fn setup_neuron_with_stake(netuid: u16, hotkey: U256, coldkey: U256, stake: increase_stake_on_coldkey_hotkey_account(&coldkey, &hotkey, stake, netuid); } +#[allow(dead_code)] +pub fn wait_set_pending_children_cooldown(netuid: u16) { + let cooldown = DefaultPendingCooldown::<Test>::get(); + step_block(cooldown as u16); // Wait for cooldown to pass + step_epochs(1, netuid); // Run next epoch +} + #[allow(dead_code)] pub fn wait_and_set_pending_children(netuid: u16) { let original_block = System::block_number(); - System::set_block_number(System::block_number() + 7300); + wait_set_pending_children_cooldown(netuid); SubtensorModule::do_set_pending_children(netuid); System::set_block_number(original_block); } #[allow(dead_code)] -pub fn mock_set_children(coldkey: &U256, parent: &U256, netuid: u16, child_vec: &[(u64, U256)]) { +pub fn mock_schedule_children( + coldkey: &U256, + parent: &U256, + netuid: u16, + child_vec: &[(u64, U256)], +) { // Set minimum stake for setting children StakeThreshold::<Test>::put(0); @@ -706,6 +719,11 @@ pub fn mock_set_children(coldkey: &U256, parent: &U256, netuid: u16, child_vec: netuid, child_vec.to_vec() )); +} + +#[allow(dead_code)] +pub fn mock_set_children(coldkey: &U256, parent: &U256, netuid: u16, child_vec: &[(u64, U256)]) { + mock_schedule_children(coldkey, parent, netuid, child_vec); wait_and_set_pending_children(netuid); } diff --git a/pallets/subtensor/src/tests/move_stake.rs b/pallets/subtensor/src/tests/move_stake.rs index 96d54ed77..b212de94e 100644 --- a/pallets/subtensor/src/tests/move_stake.rs +++ b/pallets/subtensor/src/tests/move_stake.rs @@ -242,7 +242,7 @@ fn test_do_move_nonexistent_destination_hotkey() { SubtensorModule::stake_into_subnet(&origin_hotkey, &coldkey, netuid, stake_amount, fee); // Attempt to move stake from a non-existent origin hotkey - add_network(netuid, 0, 0); + add_network(netuid, 1, 0); assert_noop!( SubtensorModule::do_move_stake( RuntimeOrigin::signed(coldkey), @@ -523,7 +523,7 @@ fn test_do_move_wrong_origin() { ); // Attempt to move stake with wrong origin - add_network(netuid, 0, 0); + add_network(netuid, 1, 0); SubtensorModule::create_account_if_non_existent(&coldkey, &origin_hotkey); SubtensorModule::create_account_if_non_existent(&coldkey, &destination_hotkey); assert_err!( diff --git a/pallets/subtensor/src/tests/registration.rs b/pallets/subtensor/src/tests/registration.rs index 07a456c83..3088f4085 100644 --- a/pallets/subtensor/src/tests/registration.rs +++ b/pallets/subtensor/src/tests/registration.rs @@ -1211,7 +1211,7 @@ fn test_registration_get_uid_to_prune_all_in_immunity_period() { new_test_ext(1).execute_with(|| { System::set_block_number(0); let netuid: u16 = 1; - add_network(netuid, 0, 0); + add_network(netuid, 1, 0); log::info!("add network"); register_ok_neuron(netuid, U256::from(0), U256::from(0), 39420842); register_ok_neuron(netuid, U256::from(1), U256::from(1), 12412392); @@ -1235,7 +1235,7 @@ fn test_registration_get_uid_to_prune_none_in_immunity_period() { new_test_ext(1).execute_with(|| { System::set_block_number(0); let netuid: u16 = 1; - add_network(netuid, 0, 0); + add_network(netuid, 1, 0); log::info!("add network"); register_ok_neuron(netuid, U256::from(0), U256::from(0), 39420842); register_ok_neuron(netuid, U256::from(1), U256::from(1), 12412392); diff --git a/pallets/subtensor/src/tests/senate.rs b/pallets/subtensor/src/tests/senate.rs index 01dfc17d7..dd198deb7 100644 --- a/pallets/subtensor/src/tests/senate.rs +++ b/pallets/subtensor/src/tests/senate.rs @@ -473,7 +473,7 @@ fn test_senate_leave_vote_removal() { // Add two networks. let root_netuid: u16 = 0; let other_netuid: u16 = 5; - add_network(other_netuid, 0, 0); + add_network(other_netuid, 1, 0); SubtensorModule::set_burn(other_netuid, 0); SubtensorModule::set_max_registrations_per_block(other_netuid, 1000); SubtensorModule::set_target_registrations_per_interval(other_netuid, 1000); diff --git a/pallets/subtensor/src/tests/staking.rs b/pallets/subtensor/src/tests/staking.rs index ef5ac5f4a..6de68a193 100644 --- a/pallets/subtensor/src/tests/staking.rs +++ b/pallets/subtensor/src/tests/staking.rs @@ -1295,7 +1295,7 @@ fn test_delegate_take_can_be_decreased() { // Register the neuron to a new network let netuid = 1; - add_network(netuid, 0, 0); + add_network(netuid, 1, 0); register_ok_neuron(netuid, hotkey0, coldkey0, 124124); // Coldkey / hotkey 0 become delegates with 9% take @@ -1330,7 +1330,7 @@ fn test_can_set_min_take_ok() { // Register the neuron to a new network let netuid = 1; - add_network(netuid, 0, 0); + add_network(netuid, 1, 0); register_ok_neuron(netuid, hotkey0, coldkey0, 124124); // Coldkey / hotkey 0 become delegates @@ -1362,7 +1362,7 @@ fn test_delegate_take_can_not_be_increased_with_decrease_take() { // Register the neuron to a new network let netuid = 1; - add_network(netuid, 0, 0); + add_network(netuid, 1, 0); register_ok_neuron(netuid, hotkey0, coldkey0, 124124); // Set min take @@ -1397,7 +1397,7 @@ fn test_delegate_take_can_be_increased() { // Register the neuron to a new network let netuid = 1; - add_network(netuid, 0, 0); + add_network(netuid, 1, 0); register_ok_neuron(netuid, hotkey0, coldkey0, 124124); // Coldkey / hotkey 0 become delegates with 9% take @@ -1432,7 +1432,7 @@ fn test_delegate_take_can_not_be_decreased_with_increase_take() { // Register the neuron to a new network let netuid = 1; - add_network(netuid, 0, 0); + add_network(netuid, 1, 0); register_ok_neuron(netuid, hotkey0, coldkey0, 124124); // Coldkey / hotkey 0 become delegates with 9% take @@ -1471,7 +1471,7 @@ fn test_delegate_take_can_be_increased_to_limit() { // Register the neuron to a new network let netuid = 1; - add_network(netuid, 0, 0); + add_network(netuid, 1, 0); register_ok_neuron(netuid, hotkey0, coldkey0, 124124); // Coldkey / hotkey 0 become delegates with 9% take @@ -1509,7 +1509,7 @@ fn test_delegate_take_can_not_be_increased_beyond_limit() { // Register the neuron to a new network let netuid = 1; - add_network(netuid, 0, 0); + add_network(netuid, 1, 0); register_ok_neuron(netuid, hotkey0, coldkey0, 124124); // Coldkey / hotkey 0 become delegates with 9% take @@ -1551,7 +1551,7 @@ fn test_rate_limits_enforced_on_increase_take() { // Register the neuron to a new network let netuid = 1; - add_network(netuid, 0, 0); + add_network(netuid, 1, 0); register_ok_neuron(netuid, hotkey0, coldkey0, 124124); // Coldkey / hotkey 0 become delegates with 9% take @@ -1678,7 +1678,7 @@ fn test_get_total_delegated_stake_no_delegations() { let coldkey = U256::from(2); let netuid = 1u16; - add_network(netuid, 0, 0); + add_network(netuid, 1, 0); register_ok_neuron(netuid, delegate, coldkey, 0); // Check that there's no delegated stake diff --git a/pallets/subtensor/src/tests/swap_hotkey.rs b/pallets/subtensor/src/tests/swap_hotkey.rs index d35dd6d56..328600cd8 100644 --- a/pallets/subtensor/src/tests/swap_hotkey.rs +++ b/pallets/subtensor/src/tests/swap_hotkey.rs @@ -222,7 +222,7 @@ fn test_swap_subnet_membership() { let netuid = 0u16; let mut weight = Weight::zero(); - add_network(netuid, 0, 1); + add_network(netuid, 1, 1); IsNetworkMember::<Test>::insert(old_hotkey, netuid, true); assert_ok!(SubtensorModule::perform_hotkey_swap( &old_hotkey, @@ -247,7 +247,7 @@ fn test_swap_uids_and_keys() { let uid = 5u16; let mut weight = Weight::zero(); - add_network(netuid, 0, 1); + add_network(netuid, 1, 1); IsNetworkMember::<Test>::insert(old_hotkey, netuid, true); Uids::<Test>::insert(netuid, old_hotkey, uid); Keys::<Test>::insert(netuid, uid, old_hotkey); @@ -276,7 +276,7 @@ fn test_swap_prometheus() { let prometheus_info = PrometheusInfo::default(); let mut weight = Weight::zero(); - add_network(netuid, 0, 1); + add_network(netuid, 1, 1); IsNetworkMember::<Test>::insert(old_hotkey, netuid, true); Prometheus::<Test>::insert(netuid, old_hotkey, prometheus_info.clone()); @@ -306,7 +306,7 @@ fn test_swap_axons() { let axon_info = AxonInfo::default(); let mut weight = Weight::zero(); - add_network(netuid, 0, 1); + add_network(netuid, 1, 1); IsNetworkMember::<Test>::insert(old_hotkey, netuid, true); Axons::<Test>::insert(netuid, old_hotkey, axon_info.clone()); @@ -333,7 +333,7 @@ fn test_swap_certificates() { let certificate = NeuronCertificate::try_from(vec![1, 2, 3]).unwrap(); let mut weight = Weight::zero(); - add_network(netuid, 0, 1); + add_network(netuid, 1, 1); IsNetworkMember::<Test>::insert(old_hotkey, netuid, true); NeuronCertificates::<Test>::insert(netuid, old_hotkey, certificate.clone()); @@ -366,7 +366,7 @@ fn test_swap_weight_commits() { weight_commits.push_back((H256::from_low_u64_be(100), 200, 1, 1)); let mut weight = Weight::zero(); - add_network(netuid, 0, 1); + add_network(netuid, 1, 1); IsNetworkMember::<Test>::insert(old_hotkey, netuid, true); WeightCommits::<Test>::insert(netuid, old_hotkey, weight_commits.clone()); @@ -397,7 +397,7 @@ fn test_swap_loaded_emission() { let validator_emission = 1000u64; let mut weight = Weight::zero(); - add_network(netuid, 0, 1); + add_network(netuid, 1, 1); IsNetworkMember::<Test>::insert(old_hotkey, netuid, true); LoadedEmission::<Test>::insert( netuid, @@ -537,8 +537,8 @@ fn test_swap_hotkey_with_multiple_subnets() { let netuid2 = 1; let mut weight = Weight::zero(); - add_network(netuid1, 0, 1); - add_network(netuid2, 0, 1); + add_network(netuid1, 1, 1); + add_network(netuid2, 1, 1); IsNetworkMember::<Test>::insert(old_hotkey, netuid1, true); IsNetworkMember::<Test>::insert(old_hotkey, netuid2, true); @@ -639,8 +639,8 @@ fn test_swap_hotkey_with_multiple_coldkeys_and_subnets() { let mut weight = Weight::zero(); // Set up initial state - add_network(netuid1, 0, 1); - add_network(netuid2, 0, 1); + add_network(netuid1, 1, 1); + add_network(netuid2, 1, 1); register_ok_neuron(netuid1, old_hotkey, coldkey1, 1234); register_ok_neuron(netuid2, old_hotkey, coldkey1, 1234); @@ -1258,7 +1258,7 @@ fn test_swap_parent_hotkey_childkey_maps() { let coldkey = U256::from(2); let child = U256::from(3); let parent_new = U256::from(4); - add_network(netuid, 0, 0); + add_network(netuid, 1, 0); SubtensorModule::create_account_if_non_existent(&coldkey, &parent_old); // Set child and verify state maps @@ -1301,7 +1301,7 @@ fn test_swap_child_hotkey_childkey_maps() { let coldkey = U256::from(2); let child_old = U256::from(3); let child_new = U256::from(4); - add_network(netuid, 0, 0); + add_network(netuid, 1, 0); SubtensorModule::create_account_if_non_existent(&coldkey, &child_old); SubtensorModule::create_account_if_non_existent(&coldkey, &parent); diff --git a/pallets/subtensor/src/tests/weights.rs b/pallets/subtensor/src/tests/weights.rs index 10c829870..ee45252a3 100644 --- a/pallets/subtensor/src/tests/weights.rs +++ b/pallets/subtensor/src/tests/weights.rs @@ -82,7 +82,7 @@ fn test_set_rootweights_validate() { }); // Create netuid - add_network(netuid, 0, 0); + add_network(netuid, 1, 0); // Register the hotkey SubtensorModule::append_neuron(netuid, &hotkey, 0); crate::Owner::<Test>::insert(hotkey, coldkey); @@ -197,7 +197,7 @@ fn test_commit_weights_validate() { }); // Create netuid - add_network(netuid, 0, 0); + add_network(netuid, 1, 0); // Register the hotkey SubtensorModule::append_neuron(netuid, &hotkey, 0); crate::Owner::<Test>::insert(hotkey, coldkey); @@ -306,7 +306,7 @@ fn test_set_weights_validate() { }); // Create netuid - add_network(netuid, 0, 0); + add_network(netuid, 1, 0); // Register the hotkey SubtensorModule::append_neuron(netuid, &hotkey, 0); crate::Owner::<Test>::insert(hotkey, coldkey); @@ -380,7 +380,7 @@ fn test_reveal_weights_validate() { }); // Create netuid - add_network(netuid, 0, 0); + add_network(netuid, 1, 0); // Register the hotkey SubtensorModule::append_neuron(netuid, &hotkey, 0); crate::Owner::<Test>::insert(hotkey, coldkey); @@ -521,7 +521,7 @@ fn test_set_stake_threshold_failed() { let hotkey = U256::from(0); let coldkey = U256::from(0); - add_network(netuid, 0, 0); + add_network(netuid, 1, 0); register_ok_neuron(netuid, hotkey, coldkey, 2143124); SubtensorModule::set_stake_threshold(20_000_000_000_000); SubtensorModule::add_balance_to_coldkey_account(&hotkey, u64::MAX); @@ -583,8 +583,8 @@ fn test_weights_version_key() { let netuid0: u16 = 1; let netuid1: u16 = 2; - add_network(netuid0, 0, 0); - add_network(netuid1, 0, 0); + add_network(netuid0, 1, 0); + add_network(netuid1, 1, 0); register_ok_neuron(netuid0, hotkey, coldkey, 2143124); register_ok_neuron(netuid1, hotkey, coldkey, 3124124); @@ -1421,7 +1421,7 @@ fn test_check_len_uids_within_allowed_not_within_network_pool() { fn test_set_weights_commit_reveal_enabled_error() { new_test_ext(0).execute_with(|| { let netuid: u16 = 1; - add_network(netuid, 0, 0); + add_network(netuid, 1, 0); register_ok_neuron(netuid, U256::from(1), U256::from(2), 10); let uids = vec![0]; diff --git a/runtime/src/lib.rs b/runtime/src/lib.rs index e0ea63987..f8491dd57 100644 --- a/runtime/src/lib.rs +++ b/runtime/src/lib.rs @@ -220,7 +220,7 @@ pub const VERSION: RuntimeVersion = RuntimeVersion { // `spec_version`, and `authoring_version` are the same between Wasm and native. // This value is set to 100 to notify Polkadot-JS App (https://polkadot.js.org/apps) to use // the compatible custom types. - spec_version: 223, + spec_version: 224, impl_version: 1, apis: RUNTIME_API_VERSIONS, transaction_version: 1, From 013fa47082bc95b013d4934a17672bcca94f9491 Mon Sep 17 00:00:00 2001 From: Cameron Fairchild <cameron@opentensor.ai> Date: Fri, 24 Jan 2025 15:31:46 -0500 Subject: [PATCH 066/145] change pool liq init --- .../subtensor/src/migrations/migrate_rao.rs | 19 ++++++++++++++----- pallets/subtensor/src/subnets/subnet.rs | 18 +++++++++--------- 2 files changed, 23 insertions(+), 14 deletions(-) diff --git a/pallets/subtensor/src/migrations/migrate_rao.rs b/pallets/subtensor/src/migrations/migrate_rao.rs index d575c07f5..f3990cc1c 100644 --- a/pallets/subtensor/src/migrations/migrate_rao.rs +++ b/pallets/subtensor/src/migrations/migrate_rao.rs @@ -71,17 +71,26 @@ pub fn migrate_rao<T: Config>() -> Weight { } let owner: T::AccountId = SubnetOwner::<T>::get(netuid); let lock: u64 = SubnetLocked::<T>::get(netuid); - let initial_liquidity: u64 = 100_000_000_000; // 100 TAO. - let remaining_lock: u64 = lock.saturating_sub(initial_liquidity); + + // Put initial TAO from lock into subnet TAO and produce numerically equal amount of Alpha + // The initial TAO is the locked amount, with a minimum of 1 TAO and a cap of 100 TAO. + let pool_initial_tao = 100_000_000_000.min(lock.max(1)); + + let remaining_lock = lock.saturating_sub(pool_initial_tao); + // Refund the owner for the remaining lock. Pallet::<T>::add_balance_to_coldkey_account(&owner, remaining_lock); - SubnetTAO::<T>::insert(netuid, initial_liquidity); // Set TAO to the lock. - SubnetAlphaIn::<T>::insert(netuid, initial_liquidity); // Set AlphaIn to the initial alpha distribution. + SubnetTAO::<T>::insert(netuid, pool_initial_tao); // Set TAO to the lock. + + SubnetAlphaIn::<T>::insert( + netuid, + pool_initial_tao.saturating_mul(netuids.len() as u64), + ); // Set AlphaIn to the initial alpha distribution. + SubnetAlphaOut::<T>::insert(netuid, 0); // Set zero subnet alpha out. SubnetMechanism::<T>::insert(netuid, 1); // Convert to dynamic immediately with initialization. Tempo::<T>::insert(netuid, DefaultTempo::<T>::get()); // Set the token symbol for this subnet using Self instead of Pallet::<T> TokenSymbol::<T>::insert(netuid, Pallet::<T>::get_symbol_for_subnet(*netuid)); - SubnetTAO::<T>::insert(netuid, initial_liquidity); // Set TAO to the lock. TotalStakeAtDynamic::<T>::insert(netuid, 0); if let Ok(owner_coldkey) = SubnetOwner::<T>::try_get(netuid) { diff --git a/pallets/subtensor/src/subnets/subnet.rs b/pallets/subtensor/src/subnets/subnet.rs index 7b7de6aeb..b2568808e 100644 --- a/pallets/subtensor/src/subnets/subnet.rs +++ b/pallets/subtensor/src/subnets/subnet.rs @@ -233,18 +233,18 @@ impl<T: Config> Pallet<T> { Self::get_symbol_for_subnet(netuid_to_register), ); // Set subnet token symbol. - // Put 100 TAO from lock into subnet TAO and produce numerically equal amount of Alpha - let mut pool_initial_tao = 100_000_000_000; - if pool_initial_tao > actual_tao_lock_amount { - pool_initial_tao = actual_tao_lock_amount; - } - if pool_initial_tao < 1 { - pool_initial_tao = 1; - } + // Put initial TAO from lock into subnet TAO and produce numerically equal amount of Alpha + // The initial TAO is the locked amount, with a minimum of 1 TAO and a cap of 100 TAO. + let pool_initial_tao = 100_000_000_000.min(actual_tao_lock_amount.max(1)); + let actual_tao_lock_amount_less_pool_tao = actual_tao_lock_amount.saturating_sub(pool_initial_tao); SubnetTAO::<T>::insert(netuid_to_register, pool_initial_tao); - SubnetAlphaIn::<T>::insert(netuid_to_register, pool_initial_tao); + SubnetAlphaIn::<T>::insert( + netuid_to_register, + pool_initial_tao.saturating_mul(Self::get_all_subnet_netuids().len() as u64), + ); // Set AlphaIn to the initial alpha distribution. + SubnetOwner::<T>::insert(netuid_to_register, coldkey.clone()); SubnetOwnerHotkey::<T>::insert(netuid_to_register, hotkey.clone()); TotalStakeAtDynamic::<T>::insert(netuid_to_register, TotalStake::<T>::get()); From 7ee3e8998d8a412bf7020da0a2fa74b2054290c5 Mon Sep 17 00:00:00 2001 From: Cameron Fairchild <cameron@opentensor.ai> Date: Fri, 24 Jan 2025 15:33:17 -0500 Subject: [PATCH 067/145] TAO -> RAO --- pallets/subtensor/src/migrations/migrate_rao.rs | 2 +- pallets/subtensor/src/subnets/subnet.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pallets/subtensor/src/migrations/migrate_rao.rs b/pallets/subtensor/src/migrations/migrate_rao.rs index f3990cc1c..2b325aadc 100644 --- a/pallets/subtensor/src/migrations/migrate_rao.rs +++ b/pallets/subtensor/src/migrations/migrate_rao.rs @@ -73,7 +73,7 @@ pub fn migrate_rao<T: Config>() -> Weight { let lock: u64 = SubnetLocked::<T>::get(netuid); // Put initial TAO from lock into subnet TAO and produce numerically equal amount of Alpha - // The initial TAO is the locked amount, with a minimum of 1 TAO and a cap of 100 TAO. + // The initial TAO is the locked amount, with a minimum of 1 RAO and a cap of 100 TAO. let pool_initial_tao = 100_000_000_000.min(lock.max(1)); let remaining_lock = lock.saturating_sub(pool_initial_tao); diff --git a/pallets/subtensor/src/subnets/subnet.rs b/pallets/subtensor/src/subnets/subnet.rs index b2568808e..942f6fe8b 100644 --- a/pallets/subtensor/src/subnets/subnet.rs +++ b/pallets/subtensor/src/subnets/subnet.rs @@ -234,7 +234,7 @@ impl<T: Config> Pallet<T> { ); // Set subnet token symbol. // Put initial TAO from lock into subnet TAO and produce numerically equal amount of Alpha - // The initial TAO is the locked amount, with a minimum of 1 TAO and a cap of 100 TAO. + // The initial TAO is the locked amount, with a minimum of 1 RAO and a cap of 100 TAO. let pool_initial_tao = 100_000_000_000.min(actual_tao_lock_amount.max(1)); let actual_tao_lock_amount_less_pool_tao = From 4dca324b89831d273d8da4167368a90a1b2b5822 Mon Sep 17 00:00:00 2001 From: Cameron Fairchild <cameron@opentensor.ai> Date: Fri, 24 Jan 2025 16:07:27 -0500 Subject: [PATCH 068/145] fix migration test for new expectation --- pallets/subtensor/src/tests/migration.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pallets/subtensor/src/tests/migration.rs b/pallets/subtensor/src/tests/migration.rs index 56cbe6cdc..793908336 100644 --- a/pallets/subtensor/src/tests/migration.rs +++ b/pallets/subtensor/src/tests/migration.rs @@ -598,9 +598,9 @@ fn test_migrate_rao() { // Verify root subnet (netuid 0) state after migration assert_eq!(SubnetTAO::<Test>::get(netuid_0), 4 * stake_amount); // Root has everything - assert_eq!(SubnetTAO::<Test>::get(netuid_1), 100_000_000_000); // Initial Rao amount. + assert_eq!(SubnetTAO::<Test>::get(netuid_1), lock_amount); // Initial Rao amount. assert_eq!(SubnetAlphaIn::<Test>::get(netuid_0), 1); // No Alpha in pool on root. - assert_eq!(SubnetAlphaIn::<Test>::get(netuid_1), 100_000_000_000); // Initial Rao amount. + assert_eq!(SubnetAlphaIn::<Test>::get(netuid_1), 2 * lock_amount); // Initial Rao amount == num_subnets * lock_amount assert_eq!(SubnetAlphaOut::<Test>::get(netuid_0), 4 * stake_amount); // All stake is outstanding. assert_eq!(SubnetAlphaOut::<Test>::get(netuid_1), 0); // No stake outstanding. From 672a379fd57851415e53881c458a19929525df09 Mon Sep 17 00:00:00 2001 From: Greg Zaitsev <gregz@opentensor.dev> Date: Fri, 24 Jan 2025 17:44:31 -0500 Subject: [PATCH 069/145] add_stake_limit in progress --- pallets/subtensor/src/epoch/math.rs | 50 ++++++- pallets/subtensor/src/macros/dispatches.rs | 99 ++++++++++++++ pallets/subtensor/src/staking/add_stake.rs | 111 ++++++++++++++++ pallets/subtensor/src/staking/remove_stake.rs | 21 +++ pallets/subtensor/src/tests/staking.rs | 125 ++++++++++++++++++ 5 files changed, 405 insertions(+), 1 deletion(-) diff --git a/pallets/subtensor/src/epoch/math.rs b/pallets/subtensor/src/epoch/math.rs index 616a9b78b..7f7ff7d9c 100644 --- a/pallets/subtensor/src/epoch/math.rs +++ b/pallets/subtensor/src/epoch/math.rs @@ -8,7 +8,7 @@ use sp_std::cmp::Ordering; use sp_std::vec; use substrate_fixed::transcendental::{exp, ln}; -use substrate_fixed::types::{I32F32, I64F64}; +use substrate_fixed::types::{I32F32, I64F64, U96F32}; // TODO: figure out what cfg gate this needs to not be a warning in rustc #[allow(unused)] @@ -1411,3 +1411,51 @@ pub fn safe_ln(value: I32F32) -> I32F32 { pub fn safe_exp(value: I32F32) -> I32F32 { exp(value).unwrap_or(I32F32::from_num(0.0)) } + +fn abs_diff(a: U96F32, b: U96F32) -> U96F32 { + if a < b { + b.saturating_sub(a) + } else { + a.saturating_sub(b) + } +} + +/// Safe sqrt with good precision +pub fn checked_sqrt(value: U96F32, epsilon: U96F32) -> Option<U96F32> { + let zero: U96F32 = U96F32::from_num(0); + let two: U96F32 = U96F32::from_num(2); + + // print!("sqrt({:?}) = ...", value); + + if value < zero { + return None; + } + + let mut high: U96F32 = value; + let mut low: U96F32 = zero; + let mut middle: U96F32 = (high + low) / two; + + let mut iteration = 0; + let max_iterations = 128; + + // Iterative approximation using bisection + while abs_diff(value.checked_div(middle).unwrap_or(zero), middle) > epsilon { + // println!("abs diff = {:?}", abs_diff(value.checked_div(middle).unwrap_or(zero), middle)); + + if value.checked_div(middle).unwrap_or(zero) < middle { + high = middle; + } else { + low = middle; + } + + middle = (high + low) / two; + + iteration += 1; + if iteration > max_iterations { + break; + } + } + + // println!("iterations = {:?}, result = {:?}", iteration, middle); + Some(middle) +} diff --git a/pallets/subtensor/src/macros/dispatches.rs b/pallets/subtensor/src/macros/dispatches.rs index d9eafffa7..2a1165b1f 100644 --- a/pallets/subtensor/src/macros/dispatches.rs +++ b/pallets/subtensor/src/macros/dispatches.rs @@ -1688,5 +1688,104 @@ mod dispatches { alpha_amount, ) } + + /// --- Adds stake to a hotkey on a subnet with a price limit. + /// This extrinsic allows to specify the limit price for alpha token + /// at which or better (lower) the staking should execute. + /// + /// In case if slippage occurs and the price shall move beyond the limit + /// price, the staking order may execute only partially or not execute + /// at all. + /// + /// # Args: + /// * 'origin': (<T as frame_system::Config>Origin): + /// - The signature of the caller's coldkey. + /// + /// * 'hotkey' (T::AccountId): + /// - The associated hotkey account. + /// + /// * 'amount_staked' (u64): + /// - The amount of stake to be added to the hotkey staking account. + /// + /// * 'limit_price' (u64): + /// - The limit price expressed in units of RAO per one Alpha. + /// + /// # Event: + /// * StakeAdded; + /// - On the successfully adding stake to a global account. + /// + /// # Raises: + /// * 'NotEnoughBalanceToStake': + /// - Not enough balance on the coldkey to add onto the global account. + /// + /// * 'NonAssociatedColdKey': + /// - The calling coldkey is not associated with this hotkey. + /// + /// * 'BalanceWithdrawalError': + /// - Errors stemming from transaction pallet. + /// + #[pallet::call_index(88)] + #[pallet::weight((Weight::from_parts(124_000_000, 0) + .saturating_add(T::DbWeight::get().reads(10)) + .saturating_add(T::DbWeight::get().writes(7)), DispatchClass::Normal, Pays::No))] + pub fn add_stake_limit( + origin: OriginFor<T>, + hotkey: T::AccountId, + netuid: u16, + amount_staked: u64, + limit_price: u64, + ) -> DispatchResult { + Self::do_add_stake_limit(origin, hotkey, netuid, amount_staked, limit_price) + } + + /// --- Removes stake from a hotkey on a subnet with a price limit. + /// This extrinsic allows to specify the limit price for alpha token + /// at which or better (higher) the staking should execute. + /// + /// In case if slippage occurs and the price shall move beyond the limit + /// price, the staking order may execute only partially or not execute + /// at all. + /// + /// # Args: + /// * 'origin': (<T as frame_system::Config>Origin): + /// - The signature of the caller's coldkey. + /// + /// * 'hotkey' (T::AccountId): + /// - The associated hotkey account. + /// + /// * 'amount_unstaked' (u64): + /// - The amount of stake to be added to the hotkey staking account. + /// + /// * 'limit_price' (u64): + /// - The limit price expressed in units of RAO per one Alpha. + /// + /// # Event: + /// * StakeRemoved; + /// - On the successfully removing stake from the hotkey account. + /// + /// # Raises: + /// * 'NotRegistered': + /// - Thrown if the account we are attempting to unstake from is non existent. + /// + /// * 'NonAssociatedColdKey': + /// - Thrown if the coldkey does not own the hotkey we are unstaking from. + /// + /// * 'NotEnoughStakeToWithdraw': + /// - Thrown if there is not enough stake on the hotkey to withdwraw this amount. + /// + #[pallet::call_index(89)] + #[pallet::weight((Weight::from_parts(111_000_000, 0) + .saturating_add(Weight::from_parts(0, 43991)) + .saturating_add(T::DbWeight::get().reads(10)) + .saturating_add(T::DbWeight::get().writes(7)), DispatchClass::Normal, Pays::No))] + pub fn remove_stake_limit( + origin: OriginFor<T>, + hotkey: T::AccountId, + netuid: u16, + amount_unstaked: u64, + limit_price: u64, + ) -> DispatchResult { + Self::do_remove_stake_limit(origin, hotkey, netuid, amount_unstaked, limit_price) + } } } diff --git a/pallets/subtensor/src/staking/add_stake.rs b/pallets/subtensor/src/staking/add_stake.rs index 66b337b3d..ca2670f12 100644 --- a/pallets/subtensor/src/staking/add_stake.rs +++ b/pallets/subtensor/src/staking/add_stake.rs @@ -1,5 +1,7 @@ use super::*; +use crate::epoch::math::*; use sp_core::Get; +use substrate_fixed::types::U96F32; impl<T: Config> Pallet<T> { /// ---- The implementation for the extrinsic add_stake: Adds stake to a hotkey account. @@ -80,4 +82,113 @@ impl<T: Config> Pallet<T> { // Ok and return. Ok(()) } + + /// ---- The implementation for the extrinsic add_stake_limit: Adds stake to a hotkey + /// account on a subnet with price limit. + /// + /// # Args: + /// * 'origin': (<T as frame_system::Config>RuntimeOrigin): + /// - The signature of the caller's coldkey. + /// + /// * 'hotkey' (T::AccountId): + /// - The associated hotkey account. + /// + /// * 'stake_to_be_added' (u64): + /// - The amount of stake to be added to the hotkey staking account. + /// + /// * 'limit_price' (u64): + /// - The limit price expressed in units of RAO per one Alpha. + /// + /// # Event: + /// * StakeAdded; + /// - On the successfully adding stake to a global account. + /// + /// # Raises: + /// * 'NotEnoughBalanceToStake': + /// - Not enough balance on the coldkey to add onto the global account. + /// + /// * 'NonAssociatedColdKey': + /// - The calling coldkey is not associated with this hotkey. + /// + /// * 'BalanceWithdrawalError': + /// - Errors stemming from transaction pallet. + /// + /// * 'TxRateLimitExceeded': + /// - Thrown if key has hit transaction rate limit + /// + pub fn do_add_stake_limit( + origin: T::RuntimeOrigin, + hotkey: T::AccountId, + netuid: u16, + stake_to_be_added: u64, + limit_price: u64, + ) -> dispatch::DispatchResult { + // TODO: Do all checks + + // Calcaulate the maximum amount that can be executed with price limit + let max_amount = Self::get_max_amount_add(netuid, limit_price); + let mut possible_stake = stake_to_be_added; + if possible_stake > max_amount { + possible_stake = max_amount; + } + + // Perform staking + // TODO + + // Ok and return. + Ok(()) + } + + // Returns the maximum amount of RAO that can be executed with price limit + pub fn get_max_amount_add(netuid: u16, limit_price: u64) -> u64 { + // Corner case: root and stao + // There's no slippage for root or stable subnets, so if limit price is 1e9 rao or + // higher, then max_amount equals u64::MAX, otherwise it is 0. + if (netuid == Self::get_root_netuid()) || (SubnetMechanism::<T>::get(netuid)) == 0 { + if limit_price >= 1_000_000_000 { + return u64::MAX; + } else { + return 0; + } + } + + // Corner case: SubnetAlphaIn is zero. Staking can't happen, so max amount is zero. + let alpha_in = SubnetAlphaIn::<T>::get(netuid); + if alpha_in == 0 { + return 0; + } + let alpha_in_float: U96F32 = U96F32::from_num(alpha_in); + + // Corner case: SubnetTAO is zero. Staking can't happen, so max amount is zero. + let tao_reserve = SubnetTAO::<T>::get(netuid); + if tao_reserve == 0 { + return 0; + } + let tao_reserve_float: U96F32 = U96F32::from_num(tao_reserve); + + // Corner case: limit_price < current_price (price cannot decrease with staking) + let limit_price_float: U96F32 = U96F32::from_num(limit_price).checked_div( + U96F32::from_num(1_000_000_000) + ).unwrap_or(U96F32::from_num(0)); + if limit_price_float < Self::get_alpha_price(netuid) { + return 0; + } + + // Main case: return SQRT(limit_price * SubnetTAO * SubnetAlphaIn) - SubnetTAO + // This is the positive solution of quare equation for finding additional TAO from + // limit_price. + let zero: U96F32 = U96F32::from_num(0.0); + let sqrt: U96F32 = checked_sqrt( + limit_price_float + .saturating_mul( + tao_reserve_float + ) + .saturating_mul( + alpha_in_float + ), + U96F32::from_num(0.1) + ).unwrap_or(zero); + + U96F32::from_num(sqrt).saturating_sub(U96F32::from_num(tao_reserve_float)).to_num::<u64>() + } } diff --git a/pallets/subtensor/src/staking/remove_stake.rs b/pallets/subtensor/src/staking/remove_stake.rs index 5b3ed3390..656939a6c 100644 --- a/pallets/subtensor/src/staking/remove_stake.rs +++ b/pallets/subtensor/src/staking/remove_stake.rs @@ -235,4 +235,25 @@ impl<T: Config> Pallet<T> { // 5. Done and ok. Ok(()) } + + pub fn do_remove_stake_limit( + origin: T::RuntimeOrigin, + hotkey: T::AccountId, + netuid: u16, + stake_to_be_added: u64, + limit_price: u64, + ) -> dispatch::DispatchResult { + // TODO: Do all checks + + // Calcaulate the maximum amount that can be executed with price limit + let _max_amount = Self::get_max_amount_remove(netuid, limit_price); + + // Ok and return. + Ok(()) + } + + // Returns the maximum amount of RAO that can be executed with price limit + pub fn get_max_amount_remove(_netuid: u16, _limit_price: u64) -> u64 { + 0 + } } diff --git a/pallets/subtensor/src/tests/staking.rs b/pallets/subtensor/src/tests/staking.rs index ef5ac5f4a..2c97a2c19 100644 --- a/pallets/subtensor/src/tests/staking.rs +++ b/pallets/subtensor/src/tests/staking.rs @@ -1,8 +1,10 @@ #![allow(clippy::unwrap_used)] #![allow(clippy::arithmetic_side_effects)] +use coinbase::block_step; use frame_support::{assert_err, assert_noop, assert_ok, traits::Currency}; use frame_system::RawOrigin; +use sp_runtime::print; use super::mock::*; use crate::*; @@ -10,6 +12,7 @@ use approx::assert_abs_diff_eq; use frame_support::dispatch::{DispatchClass, DispatchInfo, GetDispatchInfo, Pays}; use frame_support::sp_runtime::DispatchError; use sp_core::{Get, H256, U256}; +use substrate_fixed::types::{U96F32, U32F32}; /*********************************************************** staking::add_stake() tests @@ -2127,3 +2130,125 @@ fn test_stake_below_min_validate() { assert_ok!(result_min_stake); }); } + + +#[test] +fn test_stake_overflow() { + new_test_ext(0).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 amount = 100_000_000_000_000; + + let tao_reserve: U96F32 = U96F32::from_num(100_u64); + let alpha_in: U96F32 = U96F32::from_num(100_u64); + SubnetTAO::<Test>::insert(netuid, tao_reserve.to_num::<u64>()); + SubnetAlphaIn::<Test>::insert(netuid, alpha_in.to_num::<u64>()); + + println!("Price = {:?}", SubtensorModule::get_alpha_price(netuid)); + println!("SubnetAlphaIn = {:?}", SubnetAlphaIn::<Test>::get(netuid)); + println!("SubnetTAO = {:?}", SubnetTAO::<Test>::get(netuid)); + + // Give it some $$$ in his coldkey balance + SubtensorModule::add_balance_to_coldkey_account(&subnet_owner_coldkey, amount); + + // Transfer to hotkey account, and check if the result is ok + assert_ok!(SubtensorModule::add_stake( + RuntimeOrigin::signed(subnet_owner_coldkey), + subnet_owner_hotkey, + netuid, + amount + )); + + println!("Price = {:?}", SubtensorModule::get_alpha_price(netuid)); + println!("SubnetAlphaIn = {:?}", SubnetAlphaIn::<Test>::get(netuid)); + println!("SubnetTAO = {:?}", SubnetTAO::<Test>::get(netuid)); + + // Let it live for a few epochs + Tempo::<Test>::insert(netuid, 10); + step_epochs(100, netuid); + + println!("Price after = {:?}", SubtensorModule::get_alpha_price(netuid)); + + }); +} + +#[test] +fn test_max_amount_add_root() { + new_test_ext(0).execute_with(|| { + // 0 price on root => max is 0 + assert_eq!(SubtensorModule::get_max_amount_add(0, 0), 0); + + // 0.999999... price on root => max is 0 + assert_eq!(SubtensorModule::get_max_amount_add(0, 999_999_999), 0); + + // 1.0 price on root => max is u64::MAX + assert_eq!(SubtensorModule::get_max_amount_add(0, 1_000_000_000), u64::MAX); + + // 1.000...001 price on root => max is u64::MAX + assert_eq!(SubtensorModule::get_max_amount_add(0, 1_000_000_001), u64::MAX); + + // 2.0 price on root => max is u64::MAX + assert_eq!(SubtensorModule::get_max_amount_add(0, 2_000_000_000), u64::MAX); + }); +} + +#[test] +fn test_max_amount_add_stable() { + new_test_ext(0).execute_with(|| { + let netuid: u16 = 1; + add_network(netuid, 1, 0); + + // 0 price => max is 0 + assert_eq!(SubtensorModule::get_max_amount_add(netuid, 0), 0); + + // 0.999999... price => max is 0 + assert_eq!(SubtensorModule::get_max_amount_add(netuid, 999_999_999), 0); + + // 1.0 price => max is u64::MAX + assert_eq!(SubtensorModule::get_max_amount_add(netuid, 1_000_000_000), u64::MAX); + + // 1.000...001 price => max is u64::MAX + assert_eq!(SubtensorModule::get_max_amount_add(netuid, 1_000_000_001), u64::MAX); + + // 2.0 price => max is u64::MAX + assert_eq!(SubtensorModule::get_max_amount_add(netuid, 2_000_000_000), u64::MAX); + }); +} + +#[test] +fn test_max_amount_add_dynamic() { + new_test_ext(0).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); + + // Forse-set alpha in and tao reserve to make price equal 1.5 + let tao_reserve: U96F32 = U96F32::from_num(150_000_000_000_u64); + let alpha_in: U96F32 = U96F32::from_num(100_000_000_000_u64); + SubnetTAO::<Test>::insert(netuid, tao_reserve.to_num::<u64>()); + SubnetAlphaIn::<Test>::insert(netuid, alpha_in.to_num::<u64>()); + let current_price: U96F32 = U96F32::from_num(SubtensorModule::get_alpha_price(netuid)); + assert_eq!(current_price, U96F32::from_num(1.5)); + + // 0 price => max is 0 + assert_eq!(SubtensorModule::get_max_amount_add(netuid, 0), 0); + + // 1.499999... price => max is 0 + assert_eq!(SubtensorModule::get_max_amount_add(netuid, 1_499_999_999), 0); + + // 1.5 price => max is 0 because of non-zero slippage + assert_eq!(SubtensorModule::get_max_amount_add(netuid, 1_500_000_000), 0); + + // 4x price => max is 1x TAO + assert_eq!(SubtensorModule::get_max_amount_add(netuid, 6_000_000_000), 150_000_000_000); + + // 1.50000....1 price => max is 46 rao + assert_eq!(SubtensorModule::get_max_amount_add(netuid, 1_500_000_001), 46); + + // Max price doesn't panic and returns something meaningful + assert!(SubtensorModule::get_max_amount_add(netuid, u64::MAX) < 21_000_000_000_000_000); + assert!(SubtensorModule::get_max_amount_add(netuid, u64::MAX - 1) < 21_000_000_000_000_000); + assert!(SubtensorModule::get_max_amount_add(netuid, u64::MAX / 2) < 21_000_000_000_000_000); + }); +} From e955fcc2b5c871942e3f8282d6ecc7fa667dbc44 Mon Sep 17 00:00:00 2001 From: Greg Zaitsev <gregz@opentensor.dev> Date: Fri, 24 Jan 2025 17:52:58 -0500 Subject: [PATCH 070/145] Implemented add_stake_limit --- pallets/subtensor/src/staking/add_stake.rs | 49 +++++++----- pallets/subtensor/src/staking/remove_stake.rs | 6 +- pallets/subtensor/src/tests/staking.rs | 74 ++++++++++++------- 3 files changed, 84 insertions(+), 45 deletions(-) diff --git a/pallets/subtensor/src/staking/add_stake.rs b/pallets/subtensor/src/staking/add_stake.rs index 04e98575d..2f0d4875f 100644 --- a/pallets/subtensor/src/staking/add_stake.rs +++ b/pallets/subtensor/src/staking/add_stake.rs @@ -105,17 +105,33 @@ impl<T: Config> Pallet<T> { stake_to_be_added: u64, limit_price: u64, ) -> dispatch::DispatchResult { - // TODO: Do all checks + // 1. We check that the transaction is signed by the caller and retrieve the T::AccountId coldkey information. + let coldkey = ensure_signed(origin)?; + log::debug!( + "do_add_stake( origin:{:?} hotkey:{:?}, netuid:{:?}, stake_to_be_added:{:?} )", + coldkey, + hotkey, + netuid, + stake_to_be_added + ); - // Calcaulate the maximum amount that can be executed with price limit + // 2. Validate user input + Self::validate_add_stake(&coldkey, &hotkey, netuid, stake_to_be_added)?; + + // 3. Calcaulate the maximum amount that can be executed with price limit let max_amount = Self::get_max_amount_add(netuid, limit_price); let mut possible_stake = stake_to_be_added; if possible_stake > max_amount { possible_stake = max_amount; } - // Perform staking - // TODO + // 4. Ensure the remove operation from the coldkey is a success. + let tao_staked: u64 = Self::remove_balance_from_coldkey_account(&coldkey, possible_stake)?; + + // 5. Swap the stake into alpha on the subnet and increase counters. + // Emit the staking event. + let fee = DefaultStakingFee::<T>::get(); + Self::stake_into_subnet(&hotkey, &coldkey, netuid, tao_staked, fee); // Ok and return. Ok(()) @@ -149,9 +165,9 @@ impl<T: Config> Pallet<T> { let tao_reserve_float: U96F32 = U96F32::from_num(tao_reserve); // Corner case: limit_price < current_price (price cannot decrease with staking) - let limit_price_float: U96F32 = U96F32::from_num(limit_price).checked_div( - U96F32::from_num(1_000_000_000) - ).unwrap_or(U96F32::from_num(0)); + let limit_price_float: U96F32 = U96F32::from_num(limit_price) + .checked_div(U96F32::from_num(1_000_000_000)) + .unwrap_or(U96F32::from_num(0)); if limit_price_float < Self::get_alpha_price(netuid) { return 0; } @@ -162,15 +178,14 @@ impl<T: Config> Pallet<T> { let zero: U96F32 = U96F32::from_num(0.0); let sqrt: U96F32 = checked_sqrt( limit_price_float - .saturating_mul( - tao_reserve_float - ) - .saturating_mul( - alpha_in_float - ), - U96F32::from_num(0.1) - ).unwrap_or(zero); - - U96F32::from_num(sqrt).saturating_sub(U96F32::from_num(tao_reserve_float)).to_num::<u64>() + .saturating_mul(tao_reserve_float) + .saturating_mul(alpha_in_float), + U96F32::from_num(0.1), + ) + .unwrap_or(zero); + + U96F32::from_num(sqrt) + .saturating_sub(U96F32::from_num(tao_reserve_float)) + .to_num::<u64>() } } diff --git a/pallets/subtensor/src/staking/remove_stake.rs b/pallets/subtensor/src/staking/remove_stake.rs index de05d73aa..59ff7e5b3 100644 --- a/pallets/subtensor/src/staking/remove_stake.rs +++ b/pallets/subtensor/src/staking/remove_stake.rs @@ -222,10 +222,10 @@ impl<T: Config> Pallet<T> { } pub fn do_remove_stake_limit( - origin: T::RuntimeOrigin, - hotkey: T::AccountId, + _origin: T::RuntimeOrigin, + _hotkey: T::AccountId, netuid: u16, - stake_to_be_added: u64, + _stake_to_be_added: u64, limit_price: u64, ) -> dispatch::DispatchResult { // TODO: Do all checks diff --git a/pallets/subtensor/src/tests/staking.rs b/pallets/subtensor/src/tests/staking.rs index 2d5439743..233eaae78 100644 --- a/pallets/subtensor/src/tests/staking.rs +++ b/pallets/subtensor/src/tests/staking.rs @@ -1,10 +1,8 @@ #![allow(clippy::unwrap_used)] #![allow(clippy::arithmetic_side_effects)] -use coinbase::block_step; use frame_support::{assert_err, assert_noop, assert_ok, traits::Currency}; use frame_system::RawOrigin; -use sp_runtime::print; use super::mock::*; use crate::*; @@ -12,7 +10,7 @@ use approx::assert_abs_diff_eq; use frame_support::dispatch::{DispatchClass, DispatchInfo, GetDispatchInfo, Pays}; use frame_support::sp_runtime::DispatchError; use sp_core::{Get, H256, U256}; -use substrate_fixed::types::{U96F32, U32F32}; +use substrate_fixed::types::U96F32; /*********************************************************** staking::add_stake() tests @@ -2136,7 +2134,6 @@ fn test_stake_below_min_validate() { }); } - #[test] fn test_stake_overflow() { new_test_ext(0).execute_with(|| { @@ -2150,10 +2147,10 @@ fn test_stake_overflow() { SubnetTAO::<Test>::insert(netuid, tao_reserve.to_num::<u64>()); SubnetAlphaIn::<Test>::insert(netuid, alpha_in.to_num::<u64>()); - println!("Price = {:?}", SubtensorModule::get_alpha_price(netuid)); - println!("SubnetAlphaIn = {:?}", SubnetAlphaIn::<Test>::get(netuid)); - println!("SubnetTAO = {:?}", SubnetTAO::<Test>::get(netuid)); - + // println!("Price = {:?}", SubtensorModule::get_alpha_price(netuid)); + // println!("SubnetAlphaIn = {:?}", SubnetAlphaIn::<Test>::get(netuid)); + // println!("SubnetTAO = {:?}", SubnetTAO::<Test>::get(netuid)); + // Give it some $$$ in his coldkey balance SubtensorModule::add_balance_to_coldkey_account(&subnet_owner_coldkey, amount); @@ -2165,16 +2162,13 @@ fn test_stake_overflow() { amount )); - println!("Price = {:?}", SubtensorModule::get_alpha_price(netuid)); - println!("SubnetAlphaIn = {:?}", SubnetAlphaIn::<Test>::get(netuid)); - println!("SubnetTAO = {:?}", SubnetTAO::<Test>::get(netuid)); + // println!("Price = {:?}", SubtensorModule::get_alpha_price(netuid)); + // println!("SubnetAlphaIn = {:?}", SubnetAlphaIn::<Test>::get(netuid)); + // println!("SubnetTAO = {:?}", SubnetTAO::<Test>::get(netuid)); - // Let it live for a few epochs + // Let it live for a few epochs to make sure there are no panics Tempo::<Test>::insert(netuid, 10); step_epochs(100, netuid); - - println!("Price after = {:?}", SubtensorModule::get_alpha_price(netuid)); - }); } @@ -2188,13 +2182,22 @@ fn test_max_amount_add_root() { assert_eq!(SubtensorModule::get_max_amount_add(0, 999_999_999), 0); // 1.0 price on root => max is u64::MAX - assert_eq!(SubtensorModule::get_max_amount_add(0, 1_000_000_000), u64::MAX); + assert_eq!( + SubtensorModule::get_max_amount_add(0, 1_000_000_000), + u64::MAX + ); // 1.000...001 price on root => max is u64::MAX - assert_eq!(SubtensorModule::get_max_amount_add(0, 1_000_000_001), u64::MAX); + assert_eq!( + SubtensorModule::get_max_amount_add(0, 1_000_000_001), + u64::MAX + ); // 2.0 price on root => max is u64::MAX - assert_eq!(SubtensorModule::get_max_amount_add(0, 2_000_000_000), u64::MAX); + assert_eq!( + SubtensorModule::get_max_amount_add(0, 2_000_000_000), + u64::MAX + ); }); } @@ -2211,13 +2214,22 @@ fn test_max_amount_add_stable() { assert_eq!(SubtensorModule::get_max_amount_add(netuid, 999_999_999), 0); // 1.0 price => max is u64::MAX - assert_eq!(SubtensorModule::get_max_amount_add(netuid, 1_000_000_000), u64::MAX); + assert_eq!( + SubtensorModule::get_max_amount_add(netuid, 1_000_000_000), + u64::MAX + ); // 1.000...001 price => max is u64::MAX - assert_eq!(SubtensorModule::get_max_amount_add(netuid, 1_000_000_001), u64::MAX); + assert_eq!( + SubtensorModule::get_max_amount_add(netuid, 1_000_000_001), + u64::MAX + ); // 2.0 price => max is u64::MAX - assert_eq!(SubtensorModule::get_max_amount_add(netuid, 2_000_000_000), u64::MAX); + assert_eq!( + SubtensorModule::get_max_amount_add(netuid, 2_000_000_000), + u64::MAX + ); }); } @@ -2240,16 +2252,28 @@ fn test_max_amount_add_dynamic() { assert_eq!(SubtensorModule::get_max_amount_add(netuid, 0), 0); // 1.499999... price => max is 0 - assert_eq!(SubtensorModule::get_max_amount_add(netuid, 1_499_999_999), 0); + assert_eq!( + SubtensorModule::get_max_amount_add(netuid, 1_499_999_999), + 0 + ); // 1.5 price => max is 0 because of non-zero slippage - assert_eq!(SubtensorModule::get_max_amount_add(netuid, 1_500_000_000), 0); + assert_eq!( + SubtensorModule::get_max_amount_add(netuid, 1_500_000_000), + 0 + ); // 4x price => max is 1x TAO - assert_eq!(SubtensorModule::get_max_amount_add(netuid, 6_000_000_000), 150_000_000_000); + assert_eq!( + SubtensorModule::get_max_amount_add(netuid, 6_000_000_000), + 150_000_000_000 + ); // 1.50000....1 price => max is 46 rao - assert_eq!(SubtensorModule::get_max_amount_add(netuid, 1_500_000_001), 46); + assert_eq!( + SubtensorModule::get_max_amount_add(netuid, 1_500_000_001), + 46 + ); // Max price doesn't panic and returns something meaningful assert!(SubtensorModule::get_max_amount_add(netuid, u64::MAX) < 21_000_000_000_000_000); From 877007945de1245f63d881e0bef059c5b5b65f95 Mon Sep 17 00:00:00 2001 From: Cameron Fairchild <cameron@opentensor.ai> Date: Fri, 24 Jan 2025 19:42:24 -0500 Subject: [PATCH 071/145] adjust move stake tests to convert stake to alpha --- pallets/subtensor/src/tests/move_stake.rs | 48 +++++++++++++++++------ 1 file changed, 35 insertions(+), 13 deletions(-) diff --git a/pallets/subtensor/src/tests/move_stake.rs b/pallets/subtensor/src/tests/move_stake.rs index aa057ed4a..5dcab3d95 100644 --- a/pallets/subtensor/src/tests/move_stake.rs +++ b/pallets/subtensor/src/tests/move_stake.rs @@ -3,6 +3,7 @@ use crate::*; use approx::assert_abs_diff_eq; use frame_support::{assert_err, assert_noop, assert_ok}; use sp_core::{Get, U256}; +use substrate_fixed::types::I96F32; // 1. test_do_move_success // Description: Test a successful move of stake between two hotkeys in the same subnet @@ -111,14 +112,19 @@ fn test_do_move_different_subnets() { ), 0 ); + let alpha_fee: I96F32 = + I96F32::from_num(fee) / SubtensorModule::get_alpha_price(destination_netuid); + let expected_value = I96F32::from_num(alpha) + * SubtensorModule::get_alpha_price(origin_netuid) + / SubtensorModule::get_alpha_price(destination_netuid); assert_abs_diff_eq!( SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( &destination_hotkey, &coldkey, destination_netuid ), - stake_amount - 2 * fee, - epsilon = stake_amount / 1000 + (expected_value - alpha_fee).to_num::<u64>(), + epsilon = (expected_value / 1000).to_num::<u64>() ); }); } @@ -700,13 +706,17 @@ fn test_do_move_storage_updates() { ), 0 ); + let alpha_fee = + I96F32::from_num(fee) / SubtensorModule::get_alpha_price(destination_netuid); + let alpha2 = I96F32::from_num(alpha) * SubtensorModule::get_alpha_price(origin_netuid) + / SubtensorModule::get_alpha_price(destination_netuid); assert_abs_diff_eq!( SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( &destination_hotkey, &coldkey, destination_netuid ), - alpha - fee, + (alpha2 - alpha_fee).to_num::<u64>(), epsilon = alpha / 1000 ); }); @@ -1063,10 +1073,12 @@ fn test_do_transfer_different_subnets() { &destination_coldkey, destination_netuid, ); + let expected_value = I96F32::from_num(stake_amount - fee) + / SubtensorModule::get_alpha_price(destination_netuid); assert_abs_diff_eq!( dest_stake, - stake_amount - fee, - epsilon = stake_amount / 1000 + expected_value.to_num::<u64>(), + epsilon = (expected_value / 1000).to_num::<u64>() ); }); } @@ -1114,10 +1126,15 @@ fn test_do_swap_success() { &coldkey, destination_netuid, ); + let alpha_fee = + I96F32::from_num(fee) / SubtensorModule::get_alpha_price(destination_netuid); + let expected_value = I96F32::from_num(alpha_before) + * SubtensorModule::get_alpha_price(origin_netuid) + / SubtensorModule::get_alpha_price(destination_netuid); assert_abs_diff_eq!( alpha_after, - stake_amount - fee, - epsilon = stake_amount / 1000 + (expected_value - alpha_fee).to_num::<u64>(), + epsilon = (expected_value / 1000).to_num::<u64>() ); }); } @@ -1309,7 +1326,6 @@ fn test_do_swap_partial_stake() { SubtensorModule::create_account_if_non_existent(&coldkey, &hotkey); SubtensorModule::stake_into_subnet(&hotkey, &coldkey, origin_netuid, total_stake, 0); - let fee_as_alpha2 = SubtensorModule::swap_tao_for_alpha(destination_netuid, fee); let swap_amount = total_stake / 2; assert_ok!(SubtensorModule::do_swap_stake( RuntimeOrigin::signed(coldkey), @@ -1328,14 +1344,20 @@ fn test_do_swap_partial_stake() { total_stake - swap_amount, epsilon = total_stake / 1000 ); + + let alpha_fee = + I96F32::from_num(fee) / SubtensorModule::get_alpha_price(destination_netuid); + let expected_value = I96F32::from_num(swap_amount) + * SubtensorModule::get_alpha_price(origin_netuid) + / SubtensorModule::get_alpha_price(destination_netuid); assert_abs_diff_eq!( SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( &hotkey, &coldkey, destination_netuid ), - swap_amount - fee_as_alpha2, - epsilon = total_stake / 1000 + (expected_value - alpha_fee).to_num::<u64>(), + epsilon = (expected_value / 1000).to_num::<u64>() ); }); } @@ -1378,15 +1400,15 @@ fn test_do_swap_storage_updates() { 0 ); - let fee_as_alpha = SubtensorModule::swap_tao_for_alpha(destination_netuid, fee); - + let alpha_fee = + SubtensorModule::get_alpha_price(destination_netuid) * I96F32::from_num(fee); assert_abs_diff_eq!( SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( &hotkey, &coldkey, destination_netuid ), - alpha - fee_as_alpha, + alpha - alpha_fee.to_num::<u64>(), epsilon = 5 ); }); From cc44b645c9bea233a47bcc009aaf463e20fa2b6f Mon Sep 17 00:00:00 2001 From: Cameron Fairchild <cameron@opentensor.ai> Date: Fri, 24 Jan 2025 23:41:54 -0500 Subject: [PATCH 072/145] remove epoch run --- pallets/subtensor/src/tests/children.rs | 43 +++++++------------------ 1 file changed, 11 insertions(+), 32 deletions(-) diff --git a/pallets/subtensor/src/tests/children.rs b/pallets/subtensor/src/tests/children.rs index 769e47ddc..d6e0b510a 100644 --- a/pallets/subtensor/src/tests/children.rs +++ b/pallets/subtensor/src/tests/children.rs @@ -3426,38 +3426,6 @@ fn test_parent_child_chain_emission() { let hardcoded_emission: I96F32 = I96F32::from_num(1_000_000); // 1 million (adjust as needed) - let hotkey_emission: Vec<(U256, u64, u64)> = - SubtensorModule::epoch(netuid, hardcoded_emission.saturating_to_num::<u64>()); - log::info!("hotkey_emission: {:?}", hotkey_emission); - let total_emission: I96F32 = hotkey_emission - .iter() - .map(|(_, _, emission)| I96F32::from_num(*emission)) - .sum(); - - // Verify emissions match expected from CHK arrangements - let em_eps: I96F32 = I96F32::from_num(1e-4); // 4 decimal places - // A's pending emission: - assert!( - ((I96F32::from_num(hotkey_emission[0].2) / total_emission) - - I96F32::from_num(2_f64 / 3_f64 * 1_f64 / 2_f64)).abs() // 2/3 * 1/2 = 1/3; 50% -> B - <= em_eps, - "A should have pending emission of 1/3 of total emission" - ); - // B's pending emission: - assert!( - ((I96F32::from_num(hotkey_emission[1].2) / total_emission) - - (I96F32::from_num(2_f64 / 9_f64 * 1_f64 / 2_f64 + 2_f64 / 3_f64 * 1_f64 / 2_f64))).abs() // 2/9 * 1/2 + 2/3 * 1/2; 50% -> C + 50% from A - <= em_eps, - "B should have pending emission of 4/9 of total emission" - ); - // C's pending emission: - assert!( - ((I96F32::from_num(hotkey_emission[2].2) / total_emission) - - (I96F32::from_num(1_f64 / 9_f64 + 1_f64 / 2_f64 * 2_f64 / 9_f64))).abs() // 1/9 + 2/9 * 1/2; 50% from B - <= em_eps, - "C should have pending emission of 1/9 of total emission" - ); - // Set pending emission to 0 PendingEmission::<Test>::insert(netuid, 0); @@ -3521,6 +3489,17 @@ fn test_parent_child_chain_emission() { rel_stake_inc_c ); + let hotkeys = [hotkey_a, hotkey_b, hotkey_c]; + let mut total_stake_now = 0; + for (hotkey, netuid,stake) in TotalHotkeyAlpha::<Test>::iter() { + if hotkeys.contains(&hotkey) { + total_stake_now += stake; + } else { + log::info!("hotkey: {:?}, netuid: {:?}, stake: {:?}", hotkey, netuid, stake); + } + } + log::info!("total_stake_now: {:?}, total_stake_new: {:?}", total_stake_now, total_stake_new); + let eps: I96F32 = I96F32::from_num(10_000); assert!( (total_stake_new - (total_stake_old + hardcoded_emission)).abs() <= eps, From f5df32af4ddcd1242a49af5170830cddb940d385 Mon Sep 17 00:00:00 2001 From: Cameron Fairchild <cameron@opentensor.ai> Date: Fri, 24 Jan 2025 23:45:42 -0500 Subject: [PATCH 073/145] fix price math in test --- pallets/subtensor/src/tests/move_stake.rs | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/pallets/subtensor/src/tests/move_stake.rs b/pallets/subtensor/src/tests/move_stake.rs index 5dcab3d95..f299832ed 100644 --- a/pallets/subtensor/src/tests/move_stake.rs +++ b/pallets/subtensor/src/tests/move_stake.rs @@ -1401,15 +1401,18 @@ fn test_do_swap_storage_updates() { ); let alpha_fee = - SubtensorModule::get_alpha_price(destination_netuid) * I96F32::from_num(fee); + I96F32::from_num(fee) / SubtensorModule::get_alpha_price(destination_netuid); + let expected_value = I96F32::from_num(alpha) + * SubtensorModule::get_alpha_price(origin_netuid) + / SubtensorModule::get_alpha_price(destination_netuid); assert_abs_diff_eq!( SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( &hotkey, &coldkey, destination_netuid ), - alpha - alpha_fee.to_num::<u64>(), - epsilon = 5 + (expected_value - alpha_fee).to_num::<u64>(), + epsilon = (expected_value / 1000).to_num::<u64>() ); }); } From a4046a095c11bdea65c43a1ee4eee64e28705627 Mon Sep 17 00:00:00 2001 From: Cameron Fairchild <cameron@opentensor.ai> Date: Sat, 25 Jan 2025 00:46:20 -0500 Subject: [PATCH 074/145] use rem divs --- pallets/subtensor/src/coinbase/run_coinbase.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pallets/subtensor/src/coinbase/run_coinbase.rs b/pallets/subtensor/src/coinbase/run_coinbase.rs index d340271bb..2e9fe44b4 100644 --- a/pallets/subtensor/src/coinbase/run_coinbase.rs +++ b/pallets/subtensor/src/coinbase/run_coinbase.rs @@ -457,12 +457,12 @@ impl<T: Config> Pallet<T> { // 2.5.2 --- Store the alpha dividends alpha_divs .entry(hotkey_j.clone()) - .and_modify(|e| *e = e.saturating_add(alpha_divs_j.to_num::<u64>())) - .or_insert(alpha_divs_j.to_num::<u64>()); + .and_modify(|e| *e = e.saturating_add(rem_alpha_divs_j.to_num::<u64>())) + .or_insert(rem_alpha_divs_j.to_num::<u64>()); log::debug!( "Stored alpha dividends for hotkey {:?}: {:?}", hotkey_j, - alpha_divs_j.to_num::<u64>() + rem_alpha_divs_j.to_num::<u64>() ); } } From 7f67188f6b85301731988363d20d10485a0bd26f Mon Sep 17 00:00:00 2001 From: Cameron Fairchild <cameron@opentensor.ai> Date: Sun, 26 Jan 2025 19:15:25 -0500 Subject: [PATCH 075/145] test total issuance after coinbase --- pallets/subtensor/src/tests/coinbase.rs | 92 +++++++++++++++++++++++++ 1 file changed, 92 insertions(+) diff --git a/pallets/subtensor/src/tests/coinbase.rs b/pallets/subtensor/src/tests/coinbase.rs index 257ea075f..82a68ed23 100644 --- a/pallets/subtensor/src/tests/coinbase.rs +++ b/pallets/subtensor/src/tests/coinbase.rs @@ -2,6 +2,7 @@ use super::mock::*; use crate::*; +use approx::assert_abs_diff_eq; use frame_support::assert_ok; use sp_core::U256; use substrate_fixed::types::I64F64; @@ -72,3 +73,94 @@ fn test_dynamic_function_price_equal_emission() { close(alpha_out, 2 * alpha_block_emission - expected_alpha_in, 10); }); } + +// Verifies that the total issuance after the coinbase is only increased by the coinbase emission. +// Includes TAO weight. +// SKIP_WASM_BUILD=1 RUST_LOG=debug cargo test --package pallet-subtensor --lib -- tests::coinbase::test_total_issuance_after_coinbase --exact --show-output --nocapture +#[test] +fn test_total_issuance_after_coinbase() { + new_test_ext(1).execute_with(|| { + let netuid: u16 = 1; + add_network(netuid, 1, 0); + // Set TAO weight to 18% + SubtensorModule::set_tao_weight(I96F32::from_num(0.18).saturating_to_num::<u64>()); + // Set owner cut to ~11.11% + SubtensorModule::set_subnet_owner_cut(u16::MAX / 9); + let total_coinbase_emission: I96F32 = I96F32::from_num(1_123_456_789); + let epsilon: u64 = 100; + + // Define hotkeys and coldkeys + let hotkey_a: U256 = U256::from(1); + let hotkey_b: U256 = U256::from(2); + let hotkey_c: U256 = U256::from(3); + let coldkey_a: U256 = U256::from(100); + let coldkey_b: U256 = U256::from(101); + let coldkey_c: U256 = U256::from(102); + + // Register neurons with decreasing stakes + register_ok_neuron(netuid, hotkey_a, coldkey_a, 0); + register_ok_neuron(netuid, hotkey_b, coldkey_b, 0); + register_ok_neuron(netuid, hotkey_c, coldkey_c, 0); + + // Add initial stakes + SubtensorModule::add_balance_to_coldkey_account(&coldkey_a, 1_000); + SubtensorModule::add_balance_to_coldkey_account(&coldkey_b, 1_000); + SubtensorModule::add_balance_to_coldkey_account(&coldkey_c, 1_000); + + // Swap to alpha + let total_tao: I96F32 = I96F32::from_num(300_000 + 100_000 + 50_000); + let total_alpha: I96F32 = I96F32::from_num(SubtensorModule::swap_tao_for_alpha( + netuid, + total_tao.saturating_to_num::<u64>(), + )); + + // Set the stakes directly + // This avoids needing to swap tao to alpha, impacting the initial stake distribution. + SubtensorModule::increase_stake_for_hotkey_and_coldkey_on_subnet( + &hotkey_a, + &coldkey_a, + netuid, + (total_alpha * I96F32::from_num(300_000) / total_tao).saturating_to_num::<u64>(), + ); + SubtensorModule::increase_stake_for_hotkey_and_coldkey_on_subnet( + &hotkey_b, + &coldkey_b, + netuid, + (total_alpha * I96F32::from_num(100_000) / total_tao).saturating_to_num::<u64>(), + ); + SubtensorModule::increase_stake_for_hotkey_and_coldkey_on_subnet( + &hotkey_c, + &coldkey_c, + netuid, + (total_alpha * I96F32::from_num(50_000) / total_tao).saturating_to_num::<u64>(), + ); + + // Stake some to root + let stake_to_root: u64 = 10_000_000; + SubtensorModule::add_balance_to_coldkey_account(&coldkey_a, stake_to_root); + SubtensorModule::increase_stake_for_hotkey_and_coldkey_on_subnet( + &hotkey_a, + &coldkey_a, + netuid, + stake_to_root, + ); + + let alpha_price = SubtensorModule::get_alpha_price(netuid); + log::info!("alpha_price: {:?}", alpha_price); + + // Get the total issuance + let mut total_issuance_before = TotalIssuance::<Test>::get(); + log::info!("total_issuance_before: {:?}", total_issuance_before); + + // Run the coinbase + SubtensorModule::run_coinbase(total_coinbase_emission); + + // Compare + let total_issuance_after = TotalIssuance::<Test>::get(); + assert_abs_diff_eq!( + total_issuance_after, + total_issuance_before + total_coinbase_emission.saturating_to_num::<u64>(), + epsilon = epsilon + ); + }); +} From 045b317a14a12582df2c42f7a3d23d2c4cf74e7f Mon Sep 17 00:00:00 2001 From: Cameron Fairchild <cameron@opentensor.ai> Date: Sun, 26 Jan 2025 19:16:09 -0500 Subject: [PATCH 076/145] test total stake inc after coinbase --- pallets/subtensor/src/tests/coinbase.rs | 225 ++++++++++++++++++++++++ 1 file changed, 225 insertions(+) diff --git a/pallets/subtensor/src/tests/coinbase.rs b/pallets/subtensor/src/tests/coinbase.rs index 82a68ed23..f62f23e86 100644 --- a/pallets/subtensor/src/tests/coinbase.rs +++ b/pallets/subtensor/src/tests/coinbase.rs @@ -74,6 +74,231 @@ fn test_dynamic_function_price_equal_emission() { }); } +// Verifies that the total stake after the coinbase is only increased by the coinbase emission. +// Avoids TAO weight. +// SKIP_WASM_BUILD=1 RUST_LOG=debug cargo test --package pallet-subtensor --lib -- tests::coinbase::test_total_stake_after_coinbase_no_tao_weight --exact --show-output --nocapture +#[test] +fn test_total_stake_after_coinbase_no_tao_weight() { + new_test_ext(1).execute_with(|| { + let netuid: u16 = 1; + add_network(netuid, 1, 0); + // Set TAO weight to 0 + SubtensorModule::set_tao_weight(0); + // Set owner cut to ~11.11% + SubtensorModule::set_subnet_owner_cut(u16::MAX / 9); + let total_coinbase_emission: I96F32 = I96F32::from_num(1_123_456_789); + let epsilon: u64 = 100; + + // Define hotkeys and coldkeys + let hotkey_a: U256 = U256::from(1); + let hotkey_b: U256 = U256::from(2); + let hotkey_c: U256 = U256::from(3); + let coldkey_a: U256 = U256::from(100); + let coldkey_b: U256 = U256::from(101); + let coldkey_c: U256 = U256::from(102); + + // Register neurons with decreasing stakes + register_ok_neuron(netuid, hotkey_a, coldkey_a, 0); + register_ok_neuron(netuid, hotkey_b, coldkey_b, 0); + register_ok_neuron(netuid, hotkey_c, coldkey_c, 0); + + // Add initial stakes + SubtensorModule::add_balance_to_coldkey_account(&coldkey_a, 1_000); + SubtensorModule::add_balance_to_coldkey_account(&coldkey_b, 1_000); + SubtensorModule::add_balance_to_coldkey_account(&coldkey_c, 1_000); + + // Swap to alpha + let total_tao: I96F32 = I96F32::from_num(300_000 + 100_000 + 50_000); + let total_alpha: I96F32 = I96F32::from_num(SubtensorModule::swap_tao_for_alpha( + netuid, + total_tao.saturating_to_num::<u64>(), + )); + + // Set the stakes directly + // This avoids needing to swap tao to alpha, impacting the initial stake distribution. + SubtensorModule::increase_stake_for_hotkey_and_coldkey_on_subnet( + &hotkey_a, + &coldkey_a, + netuid, + (total_alpha * I96F32::from_num(300_000) / total_tao).saturating_to_num::<u64>(), + ); + SubtensorModule::increase_stake_for_hotkey_and_coldkey_on_subnet( + &hotkey_b, + &coldkey_b, + netuid, + (total_alpha * I96F32::from_num(100_000) / total_tao).saturating_to_num::<u64>(), + ); + SubtensorModule::increase_stake_for_hotkey_and_coldkey_on_subnet( + &hotkey_c, + &coldkey_c, + netuid, + (total_alpha * I96F32::from_num(50_000) / total_tao).saturating_to_num::<u64>(), + ); + + // Get the total stake on the network + let mut total_stake_before = 0; + for (hotkey, netuid_i, alpha) in TotalHotkeyAlpha::<Test>::iter() { + if netuid_i == netuid { + total_stake_before += alpha; + } else { + assert!( + alpha == 0, + "Alpha should be 0 for non-subnet hotkeys, but is {:?} on netuid {:?}", + alpha, + netuid_i + ); + } + } + + log::info!("total_stake_before: {:?}", total_stake_before); + + // Run the coinbase + SubtensorModule::run_coinbase(total_coinbase_emission); + + // Get the total stake on the network + let mut total_stake_after = 0; + for (hotkey, netuid_i, alpha) in TotalHotkeyAlpha::<Test>::iter() { + if netuid_i == netuid { + total_stake_after += alpha; + } else { + assert!( + alpha == 0, + "Alpha should be 0 for non-subnet hotkeys, but is {:?} on netuid {:?}", + alpha, + netuid_i + ); + } + } + assert_abs_diff_eq!( + total_stake_after, + total_stake_before + total_coinbase_emission.saturating_to_num::<u64>(), + epsilon = epsilon + ); + }); +} + +// Verifies that the total stake after the coinbase is only increased by the coinbase emission. +// Includes TAO weight. +// SKIP_WASM_BUILD=1 RUST_LOG=debug cargo test --package pallet-subtensor --lib -- tests::coinbase::test_total_stake_after_coinbase --exact --show-output --nocapture +#[test] +fn test_total_stake_after_coinbase() { + new_test_ext(1).execute_with(|| { + let netuid: u16 = 1; + add_network(netuid, 1, 0); + // Set TAO weight to 18% + SubtensorModule::set_tao_weight(I96F32::from_num(0.18).saturating_to_num::<u64>()); + // Set owner cut to ~11.11% + SubtensorModule::set_subnet_owner_cut(u16::MAX / 9); + let total_coinbase_emission: I96F32 = I96F32::from_num(1_123_456_789); + let epsilon: u64 = 100; + + // Define hotkeys and coldkeys + let hotkey_a: U256 = U256::from(1); + let hotkey_b: U256 = U256::from(2); + let hotkey_c: U256 = U256::from(3); + let coldkey_a: U256 = U256::from(100); + let coldkey_b: U256 = U256::from(101); + let coldkey_c: U256 = U256::from(102); + + // Register neurons with decreasing stakes + register_ok_neuron(netuid, hotkey_a, coldkey_a, 0); + register_ok_neuron(netuid, hotkey_b, coldkey_b, 0); + register_ok_neuron(netuid, hotkey_c, coldkey_c, 0); + + // Add initial stakes + SubtensorModule::add_balance_to_coldkey_account(&coldkey_a, 1_000); + SubtensorModule::add_balance_to_coldkey_account(&coldkey_b, 1_000); + SubtensorModule::add_balance_to_coldkey_account(&coldkey_c, 1_000); + + // Swap to alpha + let total_tao: I96F32 = I96F32::from_num(300_000 + 100_000 + 50_000); + let total_alpha: I96F32 = I96F32::from_num(SubtensorModule::swap_tao_for_alpha( + netuid, + total_tao.saturating_to_num::<u64>(), + )); + + // Set the stakes directly + // This avoids needing to swap tao to alpha, impacting the initial stake distribution. + SubtensorModule::increase_stake_for_hotkey_and_coldkey_on_subnet( + &hotkey_a, + &coldkey_a, + netuid, + (total_alpha * I96F32::from_num(300_000) / total_tao).saturating_to_num::<u64>(), + ); + SubtensorModule::increase_stake_for_hotkey_and_coldkey_on_subnet( + &hotkey_b, + &coldkey_b, + netuid, + (total_alpha * I96F32::from_num(100_000) / total_tao).saturating_to_num::<u64>(), + ); + SubtensorModule::increase_stake_for_hotkey_and_coldkey_on_subnet( + &hotkey_c, + &coldkey_c, + netuid, + (total_alpha * I96F32::from_num(50_000) / total_tao).saturating_to_num::<u64>(), + ); + + // Stake some to root + let stake_to_root: u64 = 10_000_000; + SubtensorModule::add_balance_to_coldkey_account(&coldkey_a, stake_to_root); + SubtensorModule::increase_stake_for_hotkey_and_coldkey_on_subnet( + &hotkey_a, + &coldkey_a, + netuid, + stake_to_root, + ); + + let alpha_price = SubtensorModule::get_alpha_price(netuid); + log::info!("alpha_price: {:?}", alpha_price); + + // Get the total stake on the network + let mut total_stake_before = 0; + for (hotkey, netuid_i, alpha) in TotalHotkeyAlpha::<Test>::iter() { + if netuid_i == netuid { + total_stake_before += alpha; + } else if netuid == SubtensorModule::get_root_netuid() { + let as_alpha: I96F32 = I96F32::from_num(alpha) / alpha_price; + total_stake_before += as_alpha.saturating_to_num::<u64>(); + } else { + assert!( + alpha == 0, + "Alpha should be 0 for non-subnet hotkeys, but is {:?} on netuid {:?}", + alpha, + netuid_i + ); + } + } + + log::info!("total_stake_before: {:?}", total_stake_before); + + // Run the coinbase + SubtensorModule::run_coinbase(total_coinbase_emission); + + // Get the total stake on the network + let mut total_stake_after = 0; + for (hotkey, netuid_i, alpha) in TotalHotkeyAlpha::<Test>::iter() { + if netuid_i == netuid { + total_stake_after += alpha; + } else if netuid == SubtensorModule::get_root_netuid() { + let as_alpha: I96F32 = I96F32::from_num(alpha) / alpha_price; + total_stake_after += as_alpha.saturating_to_num::<u64>(); + } else { + assert!( + alpha == 0, + "Alpha should be 0 for non-subnet hotkeys, but is {:?} on netuid {:?}", + alpha, + netuid_i + ); + } + } + assert_abs_diff_eq!( + total_stake_after, + total_stake_before + total_coinbase_emission.saturating_to_num::<u64>(), + epsilon = epsilon + ); + }); +} + // Verifies that the total issuance after the coinbase is only increased by the coinbase emission. // Includes TAO weight. // SKIP_WASM_BUILD=1 RUST_LOG=debug cargo test --package pallet-subtensor --lib -- tests::coinbase::test_total_issuance_after_coinbase --exact --show-output --nocapture From 2cb732c7345fde50ce7039a7271285135010a4e1 Mon Sep 17 00:00:00 2001 From: Cameron Fairchild <cameron@opentensor.ai> Date: Sun, 26 Jan 2025 19:18:28 -0500 Subject: [PATCH 077/145] use alpha emission for test --- pallets/subtensor/src/tests/children.rs | 35 ++++++++++++++++--------- 1 file changed, 23 insertions(+), 12 deletions(-) diff --git a/pallets/subtensor/src/tests/children.rs b/pallets/subtensor/src/tests/children.rs index d6e0b510a..a179fc5aa 100644 --- a/pallets/subtensor/src/tests/children.rs +++ b/pallets/subtensor/src/tests/children.rs @@ -3425,6 +3425,8 @@ fn test_parent_child_chain_emission() { SubtensorModule::set_tao_weight(0); let hardcoded_emission: I96F32 = I96F32::from_num(1_000_000); // 1 million (adjust as needed) + let emission_as_alpha = + I96F32::from_num(hardcoded_emission) / SubtensorModule::get_alpha_price(netuid); // Set pending emission to 0 PendingEmission::<Test>::insert(netuid, 0); @@ -3489,22 +3491,31 @@ fn test_parent_child_chain_emission() { rel_stake_inc_c ); - let hotkeys = [hotkey_a, hotkey_b, hotkey_c]; - let mut total_stake_now = 0; - for (hotkey, netuid,stake) in TotalHotkeyAlpha::<Test>::iter() { - if hotkeys.contains(&hotkey) { - total_stake_now += stake; - } else { - log::info!("hotkey: {:?}, netuid: {:?}, stake: {:?}", hotkey, netuid, stake); - } - } - log::info!("total_stake_now: {:?}, total_stake_new: {:?}", total_stake_now, total_stake_new); + let hotkeys = [hotkey_a, hotkey_b, hotkey_c]; + let mut total_stake_now = 0; + for (hotkey, netuid, stake) in TotalHotkeyAlpha::<Test>::iter() { + if hotkeys.contains(&hotkey) { + total_stake_now += stake; + } else { + log::info!( + "hotkey: {:?}, netuid: {:?}, stake: {:?}", + hotkey, + netuid, + stake + ); + } + } + log::info!( + "total_stake_now: {:?}, total_stake_new: {:?}", + total_stake_now, + total_stake_new + ); let eps: I96F32 = I96F32::from_num(10_000); assert!( - (total_stake_new - (total_stake_old + hardcoded_emission)).abs() <= eps, + (total_stake_new - (total_stake_old + emission_as_alpha)).abs() <= eps, "Total stake should have increased by the hardcoded emission amount {:?}", - total_stake_new - (total_stake_old + hardcoded_emission) + total_stake_new - (total_stake_old + emission_as_alpha) ); }); } From d65b2164258d46e9e016eff6fde1dcd8cffb888a Mon Sep 17 00:00:00 2001 From: Cameron Fairchild <cameron@opentensor.ai> Date: Sun, 26 Jan 2025 19:25:13 -0500 Subject: [PATCH 078/145] dont register owner twice --- pallets/subtensor/src/migrations/migrate_rao.rs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/pallets/subtensor/src/migrations/migrate_rao.rs b/pallets/subtensor/src/migrations/migrate_rao.rs index 2b325aadc..13ff02c11 100644 --- a/pallets/subtensor/src/migrations/migrate_rao.rs +++ b/pallets/subtensor/src/migrations/migrate_rao.rs @@ -98,8 +98,12 @@ pub fn migrate_rao<T: Config>() -> Weight { SubnetOwnerHotkey::<T>::insert(netuid, owner_coldkey.clone()); // Associate the coldkey to coldkey. Pallet::<T>::create_account_if_non_existent(&owner_coldkey, &owner_coldkey); - // Register the owner_coldkey as neuron to the network. - let _neuron_uid: u16 = Pallet::<T>::register_neuron(*netuid, &owner_coldkey); + + // Only register the owner coldkey if it's not already a hotkey on the subnet. + if !Uids::<T>::contains_key(*netuid, &owner_coldkey) { + // Register the owner_coldkey as neuron to the network. + let _neuron_uid: u16 = Pallet::<T>::register_neuron(*netuid, &owner_coldkey); + } // Register the neuron immediately. if !Identities::<T>::contains_key(owner_coldkey.clone()) { // Set the identitiy for the Owner coldkey if non existent. From 6cc836be8f391e5851b4c181df740e56e97b84de Mon Sep 17 00:00:00 2001 From: Cameron Fairchild <cameron@opentensor.ai> Date: Sun, 26 Jan 2025 19:28:14 -0500 Subject: [PATCH 079/145] change alpha out em to match alpha block em --- pallets/subtensor/src/coinbase/block_emission.rs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/pallets/subtensor/src/coinbase/block_emission.rs b/pallets/subtensor/src/coinbase/block_emission.rs index ab570c19e..a19651f8c 100644 --- a/pallets/subtensor/src/coinbase/block_emission.rs +++ b/pallets/subtensor/src/coinbase/block_emission.rs @@ -65,9 +65,7 @@ impl<T: Config> Pallet<T> { } // Set Alpha in emission. - let alpha_out_emission = I96F32::from_num(2) - .saturating_mul(float_alpha_block_emission) - .saturating_sub(alpha_in_emission); + let alpha_out_emission = float_alpha_block_emission; // Log results. log::debug!("{:?} - tao_in_emission: {:?}", netuid, tao_in_emission); From 4197a037605fe6af9a35b78a656c11107262ef6e Mon Sep 17 00:00:00 2001 From: Cameron Fairchild <cameron@opentensor.ai> Date: Sun, 26 Jan 2025 19:34:37 -0500 Subject: [PATCH 080/145] make sure to ensure burn reg before writing --- pallets/subtensor/src/subnets/registration.rs | 26 +++++++++---------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/pallets/subtensor/src/subnets/registration.rs b/pallets/subtensor/src/subnets/registration.rs index 342c5c408..9913578e8 100644 --- a/pallets/subtensor/src/subnets/registration.rs +++ b/pallets/subtensor/src/subnets/registration.rs @@ -119,29 +119,29 @@ impl<T: Config> Pallet<T> { Error::<T>::NotEnoughBalanceToStake ); - // --- 8. Ensure the remove operation from the coldkey is a success. - let actual_burn_amount = - Self::remove_balance_from_coldkey_account(&coldkey, registration_cost)?; - - // Tokens are swapped and then burned. - let burned_alpha: u64 = Self::swap_tao_for_alpha(netuid, actual_burn_amount); - SubnetAlphaOut::<T>::mutate(netuid, |total| *total = total.saturating_sub(burned_alpha)); - - // --- 9. If the network account does not exist we will create it here. - Self::create_account_if_non_existent(&coldkey, &hotkey); - - // --- 10. Ensure that the pairing is correct. + // --- 8. Ensure that the pairing is correct. ensure!( Self::coldkey_owns_hotkey(&coldkey, &hotkey), Error::<T>::NonAssociatedColdKey ); - // Possibly there is no neuron slots at all. + // --- 9. Possibly there are no neuron slots at all. ensure!( Self::get_max_allowed_uids(netuid) != 0, Error::<T>::NoNeuronIdAvailable ); + // --- 10. Ensure the remove operation from the coldkey is a success. + let actual_burn_amount = + Self::remove_balance_from_coldkey_account(&coldkey, registration_cost)?; + + // Tokens are swapped and then burned. + let burned_alpha: u64 = Self::swap_tao_for_alpha(netuid, actual_burn_amount); + SubnetAlphaOut::<T>::mutate(netuid, |total| *total = total.saturating_sub(burned_alpha)); + + // --- 11. If the network account does not exist we will create it here. + Self::create_account_if_non_existent(&coldkey, &hotkey); + // Actually perform the registration. let neuron_uid: u16 = Self::register_neuron(netuid, &hotkey); From c46628d583dd4dd50f4b9bf8a11c382ce19401a6 Mon Sep 17 00:00:00 2001 From: Cameron Fairchild <cameron@opentensor.ai> Date: Sun, 26 Jan 2025 19:44:52 -0500 Subject: [PATCH 081/145] fixes #1125; add pending CHK to hk swap --- pallets/subtensor/src/swap/swap_hotkey.rs | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/pallets/subtensor/src/swap/swap_hotkey.rs b/pallets/subtensor/src/swap/swap_hotkey.rs index eb1babe82..725e3c36f 100644 --- a/pallets/subtensor/src/swap/swap_hotkey.rs +++ b/pallets/subtensor/src/swap/swap_hotkey.rs @@ -403,8 +403,15 @@ impl<T: Config> Pallet<T> { } } - // 14. Swap Stake Delta for all coldkeys. - // DEPRECATED + // 14. Swap PendingChildKeys. + // PendingChildKeys( netuid, parent ) --> Vec<(proportion,child), cool_down_block> + for netuid in Self::get_all_subnet_netuids() { + if PendingChildKeys::<T>::contains_key(netuid, old_hotkey) { + let (children, cool_down_block) = PendingChildKeys::<T>::get(netuid, old_hotkey); + PendingChildKeys::<T>::remove(netuid, old_hotkey); + PendingChildKeys::<T>::insert(netuid, new_hotkey, (children, cool_down_block)); + } + } // Return successful after swapping all the relevant terms. Ok(()) From 45480ee1ca422141870b54e95db0bd602e9cf858 Mon Sep 17 00:00:00 2001 From: Cameron Fairchild <cameron@opentensor.ai> Date: Sun, 26 Jan 2025 19:49:09 -0500 Subject: [PATCH 082/145] add tests for #1125 --- pallets/subtensor/src/tests/swap_hotkey.rs | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/pallets/subtensor/src/tests/swap_hotkey.rs b/pallets/subtensor/src/tests/swap_hotkey.rs index a67e6696c..528ad4576 100644 --- a/pallets/subtensor/src/tests/swap_hotkey.rs +++ b/pallets/subtensor/src/tests/swap_hotkey.rs @@ -1257,12 +1257,16 @@ fn test_swap_parent_hotkey_childkey_maps() { let parent_old = U256::from(1); let coldkey = U256::from(2); let child = U256::from(3); + let child_other = U256::from(4); let parent_new = U256::from(4); add_network(netuid, 1, 0); SubtensorModule::create_account_if_non_existent(&coldkey, &parent_old); // Set child and verify state maps mock_set_children(&coldkey, &parent_old, netuid, &[(u64::MAX, child)]); + // Schedule some pending child keys. + mock_schedule_children(&coldkey, &parent_old, netuid, &[(u64::MAX, child_other)]); + assert_eq!( ParentKeys::<Test>::get(child, netuid), vec![(u64::MAX, parent_old)] @@ -1271,6 +1275,8 @@ fn test_swap_parent_hotkey_childkey_maps() { ChildKeys::<Test>::get(parent_old, netuid), vec![(u64::MAX, child)] ); + let existing_pending_child_keys = PendingChildKeys::<Test>::get(netuid, parent_old); + assert_eq!(existing_pending_child_keys.0, vec![(u64::MAX, child_other)]); // Swap let mut weight = Weight::zero(); @@ -1290,6 +1296,10 @@ fn test_swap_parent_hotkey_childkey_maps() { ChildKeys::<Test>::get(parent_new, netuid), vec![(u64::MAX, child)] ); + assert_eq!( + PendingChildKeys::<Test>::get(netuid, parent_new), + existing_pending_child_keys // Entry under new hotkey. + ); }) } @@ -1307,6 +1317,8 @@ fn test_swap_child_hotkey_childkey_maps() { // Set child and verify state maps mock_set_children(&coldkey, &parent, netuid, &[(u64::MAX, child_old)]); + // Schedule some pending child keys. + mock_schedule_children(&coldkey, &parent, netuid, &[(u64::MAX, child_old)]); assert_eq!( ParentKeys::<Test>::get(child_old, netuid), @@ -1316,6 +1328,8 @@ fn test_swap_child_hotkey_childkey_maps() { ChildKeys::<Test>::get(parent, netuid), vec![(u64::MAX, child_old)] ); + let existing_pending_child_keys = PendingChildKeys::<Test>::get(netuid, parent); + assert_eq!(existing_pending_child_keys.0, vec![(u64::MAX, child_old)]); // Swap let mut weight = Weight::zero(); @@ -1335,5 +1349,9 @@ fn test_swap_child_hotkey_childkey_maps() { ChildKeys::<Test>::get(parent, netuid), vec![(u64::MAX, child_new)] ); + assert_eq!( + PendingChildKeys::<Test>::get(netuid, parent), + (vec![(u64::MAX, child_new)], existing_pending_child_keys.1) // Same cooldown block. + ); }) } From 7e13ad50e482457ffaa07f1a8db5b5e95c7ee232 Mon Sep 17 00:00:00 2001 From: Cameron Fairchild <cameron@opentensor.ai> Date: Sun, 26 Jan 2025 20:02:06 -0500 Subject: [PATCH 083/145] also swap if in another hotkey's pending CHK --- pallets/subtensor/src/swap/swap_hotkey.rs | 24 +++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/pallets/subtensor/src/swap/swap_hotkey.rs b/pallets/subtensor/src/swap/swap_hotkey.rs index 725e3c36f..f8bd979df 100644 --- a/pallets/subtensor/src/swap/swap_hotkey.rs +++ b/pallets/subtensor/src/swap/swap_hotkey.rs @@ -364,6 +364,7 @@ impl<T: Config> Pallet<T> { ChildKeys::<T>::remove(old_hotkey, netuid); // Insert the same child entries for the new hotkey ChildKeys::<T>::insert(new_hotkey, netuid, my_children.clone()); + weight.saturating_accrue(T::DbWeight::get().reads_writes(1, 2)); for (_, child_key_i) in my_children { // For each child, update their parent list let mut child_parents: Vec<(u64, T::AccountId)> = @@ -376,6 +377,7 @@ impl<T: Config> Pallet<T> { } // Update the child's parent list ParentKeys::<T>::insert(child_key_i, netuid, child_parents); + weight.saturating_accrue(T::DbWeight::get().reads_writes(1, 1)); } } @@ -388,6 +390,7 @@ impl<T: Config> Pallet<T> { ParentKeys::<T>::remove(old_hotkey, netuid); // Insert the same parent entries for the new hotkey ParentKeys::<T>::insert(new_hotkey, netuid, parents.clone()); + weight.saturating_accrue(T::DbWeight::get().reads_writes(1, 2)); for (_, parent_key_i) in parents { // For each parent, update their children list let mut parent_children: Vec<(u64, T::AccountId)> = @@ -400,16 +403,37 @@ impl<T: Config> Pallet<T> { } // Update the parent's children list ChildKeys::<T>::insert(parent_key_i, netuid, parent_children); + weight.saturating_accrue(T::DbWeight::get().reads_writes(1, 1)); } } // 14. Swap PendingChildKeys. // PendingChildKeys( netuid, parent ) --> Vec<(proportion,child), cool_down_block> for netuid in Self::get_all_subnet_netuids() { + weight.saturating_accrue(T::DbWeight::get().reads(1)); if PendingChildKeys::<T>::contains_key(netuid, old_hotkey) { let (children, cool_down_block) = PendingChildKeys::<T>::get(netuid, old_hotkey); PendingChildKeys::<T>::remove(netuid, old_hotkey); PendingChildKeys::<T>::insert(netuid, new_hotkey, (children, cool_down_block)); + weight.saturating_accrue(T::DbWeight::get().reads_writes(1, 2)); + } + + // Also check for others with our hotkey as a child + for (hotkey, (children, cool_down_block)) in PendingChildKeys::<T>::iter_prefix(netuid) + { + weight.saturating_accrue(T::DbWeight::get().reads(1)); + + if let Some(potential_idx) = + children.iter().position(|(_, child)| *child == *old_hotkey) + { + let mut new_children = children.clone(); + let entry_to_remove = new_children.remove(potential_idx); + new_children.push((entry_to_remove.0, new_hotkey.clone())); // Keep the proportion. + + PendingChildKeys::<T>::remove(netuid, hotkey.clone()); + PendingChildKeys::<T>::insert(netuid, hotkey, (new_children, cool_down_block)); + weight.saturating_accrue(T::DbWeight::get().reads_writes(1, 2)); + } } } From 2ba69b19c5ce54e095ff2e511780b4c4137bf5db Mon Sep 17 00:00:00 2001 From: andreea-popescu-reef <160024917+andreea-popescu-reef@users.noreply.github.com> Date: Mon, 27 Jan 2025 20:38:23 +0800 Subject: [PATCH 084/145] reset neuron data on registration (#755) --- pallets/subtensor/src/subnets/uids.rs | 23 ++++++++++ pallets/subtensor/src/tests/uids.rs | 66 ++++++++++++++++++++++++--- 2 files changed, 82 insertions(+), 7 deletions(-) diff --git a/pallets/subtensor/src/subnets/uids.rs b/pallets/subtensor/src/subnets/uids.rs index afdbc0371..6b1fa5c6b 100644 --- a/pallets/subtensor/src/subnets/uids.rs +++ b/pallets/subtensor/src/subnets/uids.rs @@ -8,6 +8,23 @@ impl<T: Config> Pallet<T> { SubnetworkN::<T>::get(netuid) } + /// Sets value for the element at the given position if it exists. + pub fn set_element_at<N>(vec: &mut [N], position: usize, value: N) { + if let Some(element) = vec.get_mut(position) { + *element = value; + } + } + + /// Resets the trust, emission, consensus, incentive, dividends of the neuron to default + pub fn clear_neuron(netuid: u16, neuron_uid: u16) { + let neuron_index: usize = neuron_uid.into(); + Emission::<T>::mutate(netuid, |v| Self::set_element_at(v, neuron_index, 0)); + Trust::<T>::mutate(netuid, |v| Self::set_element_at(v, neuron_index, 0)); + Consensus::<T>::mutate(netuid, |v| Self::set_element_at(v, neuron_index, 0)); + Incentive::<T>::mutate(netuid, |v| Self::set_element_at(v, neuron_index, 0)); + Dividends::<T>::mutate(netuid, |v| Self::set_element_at(v, neuron_index, 0)); + } + /// Replace the neuron under this uid. pub fn replace_neuron( netuid: u16, @@ -40,6 +57,12 @@ impl<T: Config> Pallet<T> { // 4. Clear neuron certificates NeuronCertificates::<T>::remove(netuid, old_hotkey.clone()); + + // 5. Reset new neuron's values. + Self::clear_neuron(netuid, uid_to_replace); + + // 5a. reset axon info for the new uid. + Axons::<T>::remove(netuid, old_hotkey); } /// Appends the uid to the network. diff --git a/pallets/subtensor/src/tests/uids.rs b/pallets/subtensor/src/tests/uids.rs index 9fdeca041..b45a156a9 100644 --- a/pallets/subtensor/src/tests/uids.rs +++ b/pallets/subtensor/src/tests/uids.rs @@ -50,17 +50,47 @@ fn test_replace_neuron() { // Get UID let neuron_uid = SubtensorModule::get_uid_for_net_and_hotkey(netuid, &hotkey_account_id); assert_ok!(neuron_uid); + let neuron_uid = neuron_uid.unwrap(); + + // set non-default values + Trust::<Test>::mutate(netuid, |v| { + SubtensorModule::set_element_at(v, neuron_uid as usize, 5u16) + }); + Emission::<Test>::mutate(netuid, |v| { + SubtensorModule::set_element_at(v, neuron_uid as usize, 5u64) + }); + Consensus::<Test>::mutate(netuid, |v| { + SubtensorModule::set_element_at(v, neuron_uid as usize, 5u16) + }); + Incentive::<Test>::mutate(netuid, |v| { + SubtensorModule::set_element_at(v, neuron_uid as usize, 5u16) + }); + Dividends::<Test>::mutate(netuid, |v| { + SubtensorModule::set_element_at(v, neuron_uid as usize, 5u16) + }); + + // serve axon mock address + let ip: u128 = 1676056785; + let port: u16 = 9999; + let ip_type: u8 = 4; + assert!(SubtensorModule::serve_axon( + <<Test as Config>::RuntimeOrigin>::signed(hotkey_account_id), + netuid, + 0, + ip, + port, + ip_type, + 0, + 0, + 0 + ) + .is_ok()); // Set a neuron certificate for it NeuronCertificates::<Test>::insert(netuid, hotkey_account_id, certificate); // Replace the neuron. - SubtensorModule::replace_neuron( - netuid, - neuron_uid.unwrap(), - &new_hotkey_account_id, - block_number, - ); + SubtensorModule::replace_neuron(netuid, neuron_uid, &new_hotkey_account_id, block_number); // Check old hotkey is not registered on any network. assert!(SubtensorModule::get_uid_for_net_and_hotkey(netuid, &hotkey_account_id).is_err()); @@ -68,7 +98,7 @@ fn test_replace_neuron() { &hotkey_account_id )); - let curr_hotkey = SubtensorModule::get_hotkey_for_net_and_uid(netuid, neuron_uid.unwrap()); + let curr_hotkey = SubtensorModule::get_hotkey_for_net_and_uid(netuid, neuron_uid); assert_ok!(curr_hotkey); assert_ne!(curr_hotkey.unwrap(), hotkey_account_id); @@ -84,6 +114,28 @@ fn test_replace_neuron() { // Check neuron certificate was reset let certificate = NeuronCertificates::<Test>::get(netuid, hotkey_account_id); assert_eq!(certificate, None); + + // Check trust, emission, consensus, incentive, dividends have been reset to 0. + assert_eq!(SubtensorModule::get_trust_for_uid(netuid, neuron_uid), 0); + assert_eq!(SubtensorModule::get_emission_for_uid(netuid, neuron_uid), 0); + assert_eq!( + SubtensorModule::get_consensus_for_uid(netuid, neuron_uid), + 0 + ); + assert_eq!( + SubtensorModule::get_incentive_for_uid(netuid, neuron_uid), + 0 + ); + assert_eq!( + SubtensorModule::get_dividends_for_uid(netuid, neuron_uid), + 0 + ); + + // Check axon info is reset. + let axon_info = SubtensorModule::get_axon_info(netuid, &curr_hotkey.unwrap()); + assert_eq!(axon_info.ip, 0); + assert_eq!(axon_info.port, 0); + assert_eq!(axon_info.ip_type, 0); }); } From 4dd50d701c99bdd2928d92620ff911d1060e4137 Mon Sep 17 00:00:00 2001 From: Cameron Fairchild <cameron@opentensor.ai> Date: Mon, 27 Jan 2025 11:18:02 -0500 Subject: [PATCH 085/145] move create before other ensures --- pallets/subtensor/src/subnets/registration.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pallets/subtensor/src/subnets/registration.rs b/pallets/subtensor/src/subnets/registration.rs index 9913578e8..bce9ea25c 100644 --- a/pallets/subtensor/src/subnets/registration.rs +++ b/pallets/subtensor/src/subnets/registration.rs @@ -119,6 +119,9 @@ impl<T: Config> Pallet<T> { Error::<T>::NotEnoughBalanceToStake ); + // If the network account does not exist we will create it here. + Self::create_account_if_non_existent(&coldkey, &hotkey); + // --- 8. Ensure that the pairing is correct. ensure!( Self::coldkey_owns_hotkey(&coldkey, &hotkey), @@ -139,9 +142,6 @@ impl<T: Config> Pallet<T> { let burned_alpha: u64 = Self::swap_tao_for_alpha(netuid, actual_burn_amount); SubnetAlphaOut::<T>::mutate(netuid, |total| *total = total.saturating_sub(burned_alpha)); - // --- 11. If the network account does not exist we will create it here. - Self::create_account_if_non_existent(&coldkey, &hotkey); - // Actually perform the registration. let neuron_uid: u16 = Self::register_neuron(netuid, &hotkey); From a7e2ad121cb923befd57f8eb2c7f6f56e117067e Mon Sep 17 00:00:00 2001 From: Cameron Fairchild <cameron@opentensor.ai> Date: Mon, 27 Jan 2025 11:34:18 -0500 Subject: [PATCH 086/145] add test for no write --- pallets/subtensor/src/tests/registration.rs | 43 +++++++++++++++++++++ 1 file changed, 43 insertions(+) diff --git a/pallets/subtensor/src/tests/registration.rs b/pallets/subtensor/src/tests/registration.rs index 36be6f2fc..fa7c06f9e 100644 --- a/pallets/subtensor/src/tests/registration.rs +++ b/pallets/subtensor/src/tests/registration.rs @@ -490,6 +490,49 @@ fn test_burn_registration_without_neuron_slot() { }); } +#[test] +fn test_burn_registration_doesnt_write_on_failure() { + new_test_ext(1).execute_with(|| { + let netuid: u16 = 1; + let tempo: u16 = 13; + let hotkey_account_id = U256::from(1); + let burn_cost = 1000; + let initial_balance = burn_cost * 10; + let coldkey_account_id = U256::from(987); + + // Add network and set burn cost + add_network(netuid, tempo, 0); + SubtensorModule::set_burn(netuid, burn_cost); + // Give coldkey balance to pay for registration + SubtensorModule::add_balance_to_coldkey_account(&coldkey_account_id, initial_balance); + // Set max allowed uids to 0 so registration will fail, but only on last check. + SubtensorModule::set_max_allowed_uids(netuid, 0); + + // We expect this to fail at the last ensure check. + assert_err!( + SubtensorModule::burned_register( + <<Test as Config>::RuntimeOrigin>::signed(coldkey_account_id), + netuid, + hotkey_account_id + ), + Error::<Test>::NoNeuronIdAvailable + ); + + // Make sure the coldkey balance is unchanged. + assert_eq!( + SubtensorModule::get_coldkey_balance(&coldkey_account_id), + initial_balance + ); + // Make sure the neuron is not registered. + assert_eq!(SubtensorModule::get_subnetwork_n(netuid), 0); + // Make sure the hotkey is not registered. + assert_eq!( + SubtensorModule::get_uid_for_net_and_hotkey(netuid, &hotkey_account_id).is_err(), + true + ); + }); +} + #[test] fn test_burn_adjustment() { new_test_ext(1).execute_with(|| { From b0a6a78d4a1f5049d088bdc4511ca6f0638f62d7 Mon Sep 17 00:00:00 2001 From: Greg Zaitsev <gregz@opentensor.dev> Date: Mon, 27 Jan 2025 11:49:17 -0500 Subject: [PATCH 087/145] Ban saturating_div --- pallets/collective/src/lib.rs | 3 +- .../subtensor/src/coinbase/block_emission.rs | 3 +- pallets/subtensor/src/coinbase/block_step.rs | 9 +- pallets/subtensor/src/coinbase/root.rs | 3 +- .../subtensor/src/coinbase/run_coinbase.rs | 7 +- pallets/subtensor/src/epoch/math.rs | 90 +++++++++++++------ pallets/subtensor/src/epoch/run_epoch.rs | 21 +++-- .../subtensor/src/rpc_info/delegate_info.rs | 9 +- pallets/subtensor/src/staking/move_stake.rs | 3 +- pallets/subtensor/src/staking/stake_utils.rs | 7 +- pallets/subtensor/src/subnets/weights.rs | 4 +- pallets/subtensor/src/tests/children.rs | 2 +- pallets/subtensor/src/tests/epoch.rs | 12 +-- pallets/subtensor/src/utils/misc.rs | 8 +- runtime/src/lib.rs | 4 +- 15 files changed, 114 insertions(+), 71 deletions(-) diff --git a/pallets/collective/src/lib.rs b/pallets/collective/src/lib.rs index 823e92663..707f49ded 100644 --- a/pallets/collective/src/lib.rs +++ b/pallets/collective/src/lib.rs @@ -549,7 +549,8 @@ pub mod pallet { ); let threshold = T::GetVotingMembers::get_count() - .saturating_div(2) + .checked_div(2) + .unwrap_or(0) .saturating_add(1); let members = Self::members(); diff --git a/pallets/subtensor/src/coinbase/block_emission.rs b/pallets/subtensor/src/coinbase/block_emission.rs index ab570c19e..dbc93cf93 100644 --- a/pallets/subtensor/src/coinbase/block_emission.rs +++ b/pallets/subtensor/src/coinbase/block_emission.rs @@ -1,4 +1,5 @@ use super::*; +use crate::epoch::math::*; use frame_support::traits::Get; use substrate_fixed::{transcendental::log2, types::I96F32}; @@ -139,7 +140,7 @@ impl<T: Config> Pallet<T> { for _ in 0..floored_residual_int { multiplier = multiplier.saturating_mul(I96F32::from_num(2.0)); } - let block_emission_percentage: I96F32 = I96F32::from_num(1.0).saturating_div(multiplier); + let block_emission_percentage: I96F32 = I96F32::from_num(1.0).safe_div(multiplier); // Calculate the actual emission based on the emission rate let block_emission: I96F32 = block_emission_percentage .saturating_mul(I96F32::from_num(DefaultBlockEmission::<T>::get())); diff --git a/pallets/subtensor/src/coinbase/block_step.rs b/pallets/subtensor/src/coinbase/block_step.rs index 23218ed50..ce793def9 100644 --- a/pallets/subtensor/src/coinbase/block_step.rs +++ b/pallets/subtensor/src/coinbase/block_step.rs @@ -1,4 +1,5 @@ use super::*; +use crate::epoch::math::*; use frame_support::storage::IterableStorageMap; use substrate_fixed::types::{I110F18, I96F32}; @@ -202,11 +203,11 @@ impl<T: Config + pallet_drand::Config> Pallet<T> { .saturating_mul(I110F18::from_num( registrations_this_interval.saturating_add(target_registrations_per_interval), )) - .saturating_div(I110F18::from_num( + .safe_div(I110F18::from_num( target_registrations_per_interval.saturating_add(target_registrations_per_interval), )); let alpha: I110F18 = I110F18::from_num(Self::get_adjustment_alpha(netuid)) - .saturating_div(I110F18::from_num(u64::MAX)); + .safe_div(I110F18::from_num(u64::MAX)); let next_value: I110F18 = alpha .saturating_mul(I110F18::from_num(current_difficulty)) .saturating_add( @@ -236,11 +237,11 @@ impl<T: Config + pallet_drand::Config> Pallet<T> { .saturating_mul(I110F18::from_num( registrations_this_interval.saturating_add(target_registrations_per_interval), )) - .saturating_div(I110F18::from_num( + .safe_div(I110F18::from_num( target_registrations_per_interval.saturating_add(target_registrations_per_interval), )); let alpha: I110F18 = I110F18::from_num(Self::get_adjustment_alpha(netuid)) - .saturating_div(I110F18::from_num(u64::MAX)); + .safe_div(I110F18::from_num(u64::MAX)); let next_value: I110F18 = alpha .saturating_mul(I110F18::from_num(current_burn)) .saturating_add( diff --git a/pallets/subtensor/src/coinbase/root.rs b/pallets/subtensor/src/coinbase/root.rs index 112c88785..165526ac2 100644 --- a/pallets/subtensor/src/coinbase/root.rs +++ b/pallets/subtensor/src/coinbase/root.rs @@ -16,6 +16,7 @@ // DEALINGS IN THE SOFTWARE. use super::*; +use crate::epoch::math::*; use frame_support::dispatch::Pays; use frame_support::storage::IterableStorageDoubleMap; use frame_support::weights::Weight; @@ -624,7 +625,7 @@ impl<T: Config> Pallet<T> { let mut lock_cost = last_lock.saturating_mul(mult).saturating_sub( last_lock - .saturating_div(lock_reduction_interval) + .safe_div(lock_reduction_interval) .saturating_mul(current_block.saturating_sub(last_lock_block)), ); diff --git a/pallets/subtensor/src/coinbase/run_coinbase.rs b/pallets/subtensor/src/coinbase/run_coinbase.rs index e86c07c24..e15dc15d3 100644 --- a/pallets/subtensor/src/coinbase/run_coinbase.rs +++ b/pallets/subtensor/src/coinbase/run_coinbase.rs @@ -1,4 +1,5 @@ use super::*; +use crate::epoch::math::*; use alloc::collections::BTreeMap; use substrate_fixed::types::I96F32; use tle::stream_ciphers::AESGCMStreamCipherProvider; @@ -557,7 +558,7 @@ impl<T: Config> Pallet<T> { for (proportion, _) in childkeys { remaining_proportion = remaining_proportion.saturating_sub( I96F32::from_num(proportion) // Normalize - .saturating_div(I96F32::from_num(u64::MAX)), + .safe_div(I96F32::from_num(u64::MAX)), ); } @@ -608,7 +609,7 @@ impl<T: Config> Pallet<T> { let validating_emission: I96F32 = I96F32::from_num(dividends); let childkey_take_proportion: I96F32 = I96F32::from_num(Self::get_childkey_take(hotkey, netuid)) - .saturating_div(I96F32::from_num(u16::MAX)); + .safe_div(I96F32::from_num(u16::MAX)); log::debug!( "Childkey take proportion: {:?} for hotkey {:?}", childkey_take_proportion, @@ -656,7 +657,7 @@ impl<T: Config> Pallet<T> { for (proportion, parent) in Self::get_parents(hotkey, netuid) { // Convert the parent's stake proportion to a fractional value let parent_proportion: I96F32 = - I96F32::from_num(proportion).saturating_div(I96F32::from_num(u64::MAX)); + I96F32::from_num(proportion).safe_div(I96F32::from_num(u64::MAX)); // Get the parent's root and subnet-specific (alpha) stakes let parent_root: I96F32 = I96F32::from_num(Self::get_stake_for_hotkey_on_subnet( diff --git a/pallets/subtensor/src/epoch/math.rs b/pallets/subtensor/src/epoch/math.rs index 616a9b78b..926375446 100644 --- a/pallets/subtensor/src/epoch/math.rs +++ b/pallets/subtensor/src/epoch/math.rs @@ -8,7 +8,7 @@ use sp_std::cmp::Ordering; use sp_std::vec; use substrate_fixed::transcendental::{exp, ln}; -use substrate_fixed::types::{I32F32, I64F64}; +use substrate_fixed::types::{I110F18, I32F32, I64F64, I96F32, U64F64}; // TODO: figure out what cfg gate this needs to not be a warning in rustc #[allow(unused)] @@ -51,7 +51,7 @@ pub fn u16_to_fixed(x: u16) -> I32F32 { #[allow(dead_code)] pub fn u16_proportion_to_fixed(x: u16) -> I32F32 { - I32F32::from_num(x).saturating_div(I32F32::from_num(u16::MAX)) + I32F32::from_num(x).safe_div(I32F32::from_num(u16::MAX)) } #[allow(dead_code)] @@ -107,7 +107,7 @@ pub fn vec_max_upscale_to_u16(vec: &[I32F32]) -> Vec<u16> { return vec .iter() .map(|e: &I32F32| { - e.saturating_mul(u16_max.saturating_div(*val)) + e.saturating_mul(u16_max.safe_div(*val)) .round() .to_num::<u16>() }) @@ -116,7 +116,7 @@ pub fn vec_max_upscale_to_u16(vec: &[I32F32]) -> Vec<u16> { vec.iter() .map(|e: &I32F32| { e.saturating_mul(u16_max) - .saturating_div(*val) + .safe_div(*val) .round() .to_num::<u16>() }) @@ -125,11 +125,7 @@ pub fn vec_max_upscale_to_u16(vec: &[I32F32]) -> Vec<u16> { None => { let sum: I32F32 = vec.iter().sum(); vec.iter() - .map(|e: &I32F32| { - e.saturating_mul(u16_max) - .saturating_div(sum) - .to_num::<u16>() - }) + .map(|e: &I32F32| e.saturating_mul(u16_max).safe_div(sum).to_num::<u16>()) .collect() } } @@ -145,8 +141,7 @@ pub fn vec_u16_max_upscale_to_u16(vec: &[u16]) -> Vec<u16> { #[allow(dead_code)] // Checks if u16 vector, when normalized, has a max value not greater than a u16 ratio max_limit. pub fn check_vec_max_limited(vec: &[u16], max_limit: u16) -> bool { - let max_limit_fixed: I32F32 = - I32F32::from_num(max_limit).saturating_div(I32F32::from_num(u16::MAX)); + let max_limit_fixed: I32F32 = I32F32::from_num(max_limit).safe_div(I32F32::from_num(u16::MAX)); let mut vec_fixed: Vec<I32F32> = vec.iter().map(|e: &u16| I32F32::from_num(*e)).collect(); inplace_normalize(&mut vec_fixed); let max_value: Option<&I32F32> = vec_fixed.iter().max(); @@ -219,7 +214,7 @@ pub fn sigmoid_safe(input: I32F32, rho: I32F32, kappa: I32F32) -> I32F32 { let exp_input: I32F32 = neg_rho.saturating_mul(offset); // -rho*(input-kappa) let exp_output: I32F32 = exp_safe(exp_input); // exp(-rho*(input-kappa)) let denominator: I32F32 = exp_output.saturating_add(one); // 1 + exp(-rho*(input-kappa)) - let sigmoid_output: I32F32 = one.saturating_div(denominator); // 1 / (1 + exp(-rho*(input-kappa))) + let sigmoid_output: I32F32 = one.safe_div(denominator); // 1 / (1 + exp(-rho*(input-kappa))) sigmoid_output } @@ -244,7 +239,7 @@ pub fn is_topk(vector: &[I32F32], k: usize) -> Vec<bool> { pub fn normalize(x: &[I32F32]) -> Vec<I32F32> { let x_sum: I32F32 = sum(x); if x_sum != I32F32::from_num(0.0_f32) { - x.iter().map(|xi| xi.saturating_div(x_sum)).collect() + x.iter().map(|xi| xi.safe_div(x_sum)).collect() } else { x.to_vec() } @@ -258,7 +253,7 @@ pub fn inplace_normalize(x: &mut [I32F32]) { return; } x.iter_mut() - .for_each(|value| *value = value.saturating_div(x_sum)); + .for_each(|value| *value = value.safe_div(x_sum)); } // Normalizes (sum to 1 except 0) the input vector directly in-place, using the sum arg. @@ -268,7 +263,7 @@ pub fn inplace_normalize_using_sum(x: &mut [I32F32], x_sum: I32F32) { return; } x.iter_mut() - .for_each(|value| *value = value.saturating_div(x_sum)); + .for_each(|value| *value = value.safe_div(x_sum)); } // Normalizes (sum to 1 except 0) the I64F64 input vector directly in-place. @@ -279,7 +274,7 @@ pub fn inplace_normalize_64(x: &mut [I64F64]) { return; } x.iter_mut() - .for_each(|value| *value = value.saturating_div(x_sum)); + .for_each(|value| *value = value.safe_div(x_sum)); } /// Normalizes (sum to 1 except 0) each row (dim=0) of a I64F64 matrix in-place. @@ -289,7 +284,7 @@ pub fn inplace_row_normalize_64(x: &mut [Vec<I64F64>]) { let row_sum: I64F64 = row.iter().sum(); if row_sum > I64F64::from_num(0.0_f64) { row.iter_mut() - .for_each(|x_ij: &mut I64F64| *x_ij = x_ij.saturating_div(row_sum)); + .for_each(|x_ij: &mut I64F64| *x_ij = x_ij.safe_div(row_sum)); } } } @@ -302,7 +297,7 @@ pub fn vecdiv(x: &[I32F32], y: &[I32F32]) -> Vec<I32F32> { .zip(y) .map(|(x_i, y_i)| { if *y_i != 0 { - x_i.saturating_div(*y_i) + x_i.safe_div(*y_i) } else { I32F32::from_num(0) } @@ -317,7 +312,7 @@ pub fn inplace_row_normalize(x: &mut [Vec<I32F32>]) { let row_sum: I32F32 = row.iter().sum(); if row_sum > I32F32::from_num(0.0_f32) { row.iter_mut() - .for_each(|x_ij: &mut I32F32| *x_ij = x_ij.saturating_div(row_sum)); + .for_each(|x_ij: &mut I32F32| *x_ij = x_ij.safe_div(row_sum)); } } } @@ -330,7 +325,7 @@ pub fn inplace_row_normalize_sparse(sparse_matrix: &mut [Vec<(u16, I32F32)>]) { if row_sum > I32F32::from_num(0.0) { sparse_row .iter_mut() - .for_each(|(_j, value)| *value = value.saturating_div(row_sum)); + .for_each(|(_j, value)| *value = value.safe_div(row_sum)); } } } @@ -400,7 +395,7 @@ pub fn inplace_col_normalize_sparse(sparse_matrix: &mut [Vec<(u16, I32F32)>], co if col_sum[*j as usize] == I32F32::from_num(0.0_f32) { continue; } - *value = value.saturating_div(col_sum[*j as usize]); + *value = value.safe_div(col_sum[*j as usize]); } } } @@ -428,7 +423,7 @@ pub fn inplace_col_normalize(x: &mut [Vec<I32F32>]) { .zip(&col_sums) .filter(|(_, col_sum)| **col_sum != I32F32::from_num(0_f32)) .for_each(|(m_val, col_sum)| { - *m_val = m_val.saturating_div(*col_sum); + *m_val = m_val.safe_div(*col_sum); }); }); } @@ -449,7 +444,7 @@ pub fn inplace_col_max_upscale_sparse(sparse_matrix: &mut [Vec<(u16, I32F32)>], if col_max[*j as usize] == I32F32::from_num(0.0_f32) { continue; } - *value = value.saturating_div(col_max[*j as usize]); + *value = value.safe_div(col_max[*j as usize]); } } } @@ -477,7 +472,7 @@ pub fn inplace_col_max_upscale(x: &mut [Vec<I32F32>]) { .zip(&col_maxes) .filter(|(_, col_max)| **col_max != I32F32::from_num(0)) .for_each(|(m_val, col_max)| { - *m_val = m_val.saturating_div(*col_max); + *m_val = m_val.safe_div(*col_max); }); }); } @@ -898,7 +893,7 @@ pub fn weighted_median( return score[partition_idx[0]]; } assert!(stake.len() == score.len()); - let mid_idx: usize = n.saturating_div(2); + let mid_idx: usize = n.safe_div(2); let pivot: I32F32 = score[partition_idx[mid_idx]]; let mut lo_stake: I32F32 = I32F32::from_num(0); let mut hi_stake: I32F32 = I32F32::from_num(0); @@ -1411,3 +1406,48 @@ pub fn safe_ln(value: I32F32) -> I32F32 { pub fn safe_exp(value: I32F32) -> I32F32 { exp(value).unwrap_or(I32F32::from_num(0.0)) } + +/// Safe division trait +pub trait SafeDiv { + /// Safe division that returns supplied default value for division by zero + fn safe_div_or(self, rhs: Self, def: Self) -> Self; + /// Safe division that returns default value for division by zero + fn safe_div(self, rhs: Self) -> Self; +} + +/// Implementation of safe division trait for primitive types +macro_rules! impl_safe_div_for_primitive { + ($($t:ty),*) => { + $( + impl SafeDiv for $t { + fn safe_div_or(self, rhs: Self, def: Self) -> Self { + self.checked_div(rhs).unwrap_or(def) + } + + fn safe_div(self, rhs: Self) -> Self { + self.checked_div(rhs).unwrap_or_default() + } + } + )* + }; +} +impl_safe_div_for_primitive!(u8, u16, u32, u64, i8, i16, i32, i64, usize); + +/// Implementation of safe division trait for substrate fixed types +macro_rules! impl_safe_div_for_fixed { + ($($t:ty),*) => { + $( + impl SafeDiv for $t { + fn safe_div_or(self, rhs: Self, def: Self) -> Self { + self.checked_div(rhs).unwrap_or(def) + } + + fn safe_div(self, rhs: Self) -> Self { + self.checked_div(rhs).unwrap_or_default() + } + } + )* + }; +} + +impl_safe_div_for_fixed!(I96F32, I32F32, I64F64, I110F18, U64F64); diff --git a/pallets/subtensor/src/epoch/run_epoch.rs b/pallets/subtensor/src/epoch/run_epoch.rs index e6edd2585..9b3023818 100644 --- a/pallets/subtensor/src/epoch/run_epoch.rs +++ b/pallets/subtensor/src/epoch/run_epoch.rs @@ -735,10 +735,10 @@ impl<T: Config> Pallet<T> { I32F32::from_num(Self::get_rho(netuid)) } pub fn get_float_kappa(netuid: u16) -> I32F32 { - I32F32::from_num(Self::get_kappa(netuid)).saturating_div(I32F32::from_num(u16::MAX)) + I32F32::from_num(Self::get_kappa(netuid)).safe_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)) + I32F32::from_num(Self::get_bonds_penalty(netuid)).safe_div(I32F32::from_num(u16::MAX)) } pub fn get_block_at_registration(netuid: u16) -> Vec<u64> { @@ -864,19 +864,18 @@ impl<T: Config> Pallet<T> { // Calculate the slope 'a' of the logistic function. // a = (ln((1 / alpha_high - 1)) - ln((1 / alpha_low - 1))) / (consensus_low - consensus_high) let a = (safe_ln( - (I32F32::from_num(1.0).saturating_div(alpha_high)) - .saturating_sub(I32F32::from_num(1.0)), + (I32F32::from_num(1.0).safe_div(alpha_high)).saturating_sub(I32F32::from_num(1.0)), ) .saturating_sub(safe_ln( - (I32F32::from_num(1.0).saturating_div(alpha_low)).saturating_sub(I32F32::from_num(1.0)), + (I32F32::from_num(1.0).safe_div(alpha_low)).saturating_sub(I32F32::from_num(1.0)), ))) - .saturating_div(consensus_low.saturating_sub(consensus_high)); + .safe_div(consensus_low.saturating_sub(consensus_high)); log::trace!("a: {:?}", a); // Calculate the intercept 'b' of the logistic function. // b = ln((1 / alpha_low - 1)) + a * consensus_low let b = safe_ln( - (I32F32::from_num(1.0).saturating_div(alpha_low)).saturating_sub(I32F32::from_num(1.0)), + (I32F32::from_num(1.0).safe_div(alpha_low)).saturating_sub(I32F32::from_num(1.0)), ) .saturating_add(a.saturating_mul(consensus_low)); log::trace!("b: {:?}", b); @@ -905,7 +904,7 @@ impl<T: Config> Pallet<T> { // Compute the alpha value using the logistic function formula. // alpha = 1 / (1 + exp_val) - I32F32::from_num(1.0).saturating_div(I32F32::from_num(1.0).saturating_add(exp_val)) + I32F32::from_num(1.0).safe_div(I32F32::from_num(1.0).saturating_add(exp_val)) }) .collect(); @@ -1020,7 +1019,7 @@ impl<T: Config> Pallet<T> { ) -> Vec<Vec<(u16, I32F32)>> { // Retrieve the bonds moving average for the given network ID and scale it down. let bonds_moving_average: I64F64 = I64F64::from_num(Self::get_bonds_moving_average(netuid)) - .saturating_div(I64F64::from_num(1_000_000)); + .safe_div(I64F64::from_num(1_000_000)); // Calculate the alpha value for the EMA calculation. // Alpha is derived by subtracting the scaled bonds moving average from 1. @@ -1053,7 +1052,7 @@ impl<T: Config> Pallet<T> { ) -> Vec<Vec<I32F32>> { // Retrieve the bonds moving average for the given network ID and scale it down. let bonds_moving_average: I64F64 = I64F64::from_num(Self::get_bonds_moving_average(netuid)) - .saturating_div(I64F64::from_num(1_000_000)); + .safe_div(I64F64::from_num(1_000_000)); // Calculate the alpha value for the EMA calculation. // Alpha is derived by subtracting the scaled bonds moving average from 1. @@ -1221,7 +1220,7 @@ impl<T: Config> Pallet<T> { ); let max_u16: u32 = u16::MAX as u32; // 65535 - let min_alpha_high: u16 = (max_u16.saturating_mul(4).saturating_div(5)) as u16; // 52428 + let min_alpha_high: u16 = (max_u16.saturating_mul(4).safe_div(5)) as u16; // 52428 // --- 4. Ensure alpha high is greater than the minimum ensure!(alpha_high >= min_alpha_high, Error::<T>::AlphaHighTooLow); diff --git a/pallets/subtensor/src/rpc_info/delegate_info.rs b/pallets/subtensor/src/rpc_info/delegate_info.rs index 5776c48af..ec8d48110 100644 --- a/pallets/subtensor/src/rpc_info/delegate_info.rs +++ b/pallets/subtensor/src/rpc_info/delegate_info.rs @@ -1,4 +1,5 @@ use super::*; +use crate::epoch::math::*; use frame_support::pallet_prelude::{Decode, Encode}; use frame_support::storage::IterableStorageMap; use frame_support::IterableStorageDoubleMap; @@ -27,14 +28,14 @@ impl<T: Config> Pallet<T> { emissions_per_day: U64F64, ) -> U64F64 { // Get the take as a percentage and subtract it from 1 for remainder. - let without_take: U64F64 = U64F64::from_num(1) - .saturating_sub(U64F64::from_num(take.0).saturating_div(u16::MAX.into())); + let without_take: U64F64 = + U64F64::from_num(1).saturating_sub(U64F64::from_num(take.0).safe_div(u16::MAX.into())); if total_stake > U64F64::from_num(0) { emissions_per_day .saturating_mul(without_take) // Divide by 1000 TAO for return per 1k - .saturating_div(total_stake.saturating_div(U64F64::from_num(1000.0 * 1e9))) + .safe_div(total_stake.safe_div(U64F64::from_num(1000.0 * 1e9))) } else { U64F64::from_num(0) } @@ -78,7 +79,7 @@ impl<T: Config> Pallet<T> { let emission: U64F64 = Self::get_emission_for_uid(*netuid, uid).into(); let tempo: U64F64 = Self::get_tempo(*netuid).into(); if tempo > U64F64::from_num(0) { - let epochs_per_day: U64F64 = U64F64::from_num(7200).saturating_div(tempo); + let epochs_per_day: U64F64 = U64F64::from_num(7200).safe_div(tempo); emissions_per_day = emissions_per_day.saturating_add(emission.saturating_mul(epochs_per_day)); } diff --git a/pallets/subtensor/src/staking/move_stake.rs b/pallets/subtensor/src/staking/move_stake.rs index 4644b3213..88dd1463f 100644 --- a/pallets/subtensor/src/staking/move_stake.rs +++ b/pallets/subtensor/src/staking/move_stake.rs @@ -1,4 +1,5 @@ use super::*; +use crate::epoch::math::*; use sp_core::Get; impl<T: Config> Pallet<T> { @@ -223,7 +224,7 @@ impl<T: Config> Pallet<T> { )?; // Unstake from the origin subnet, returning TAO (or a 1:1 equivalent). - let fee = DefaultStakingFee::<T>::get().saturating_div(2); + let fee = DefaultStakingFee::<T>::get().safe_div(2); let tao_unstaked = Self::unstake_from_subnet( origin_hotkey, origin_coldkey, diff --git a/pallets/subtensor/src/staking/stake_utils.rs b/pallets/subtensor/src/staking/stake_utils.rs index f34a5ace0..bb30b5c34 100644 --- a/pallets/subtensor/src/staking/stake_utils.rs +++ b/pallets/subtensor/src/staking/stake_utils.rs @@ -1,4 +1,5 @@ use super::*; +use crate::epoch::math::*; use share_pool::{SharePool, SharePoolDataOperations}; use sp_std::ops::Neg; use substrate_fixed::types::{I64F64, I96F32, U64F64}; @@ -70,7 +71,7 @@ impl<T: Config> Pallet<T> { // Step 3: Normalize the weight by dividing by u64::MAX // This ensures the result is always between 0 and 1 - weight_fixed.saturating_div(I96F32::from_num(u64::MAX)) + weight_fixed.safe_div(I96F32::from_num(u64::MAX)) } /// Sets the global global weight in storage. @@ -234,7 +235,7 @@ impl<T: Config> Pallet<T> { for (proportion, _) in children { // Convert the proportion to a normalized value between 0 and 1. let normalized_proportion: I96F32 = - I96F32::from_num(proportion).saturating_div(I96F32::from_num(u64::MAX)); + I96F32::from_num(proportion).safe_div(I96F32::from_num(u64::MAX)); log::trace!( "Normalized proportion for child: {:?}", normalized_proportion @@ -264,7 +265,7 @@ impl<T: Config> Pallet<T> { // Convert the proportion to a normalized value between 0 and 1. let normalized_proportion: I96F32 = - I96F32::from_num(proportion).saturating_div(I96F32::from_num(u64::MAX)); + I96F32::from_num(proportion).safe_div(I96F32::from_num(u64::MAX)); log::trace!( "Normalized proportion from parent: {:?}", normalized_proportion diff --git a/pallets/subtensor/src/subnets/weights.rs b/pallets/subtensor/src/subnets/weights.rs index fca0e5999..13fa0dafc 100644 --- a/pallets/subtensor/src/subnets/weights.rs +++ b/pallets/subtensor/src/subnets/weights.rs @@ -1001,9 +1001,7 @@ impl<T: Config> Pallet<T> { return weights; } weights.iter_mut().for_each(|x| { - *x = (*x as u64) - .saturating_mul(u16::MAX as u64) - .saturating_div(sum) as u16; + *x = (*x as u64).saturating_mul(u16::MAX as u64).safe_div(sum) as u16; }); weights } diff --git a/pallets/subtensor/src/tests/children.rs b/pallets/subtensor/src/tests/children.rs index 30fe8287b..7a3780c0b 100644 --- a/pallets/subtensor/src/tests/children.rs +++ b/pallets/subtensor/src/tests/children.rs @@ -3702,7 +3702,7 @@ fn test_dynamic_parent_child_relationships() { let expected_parent_stake = ((I96F32::from_num(stake_parent_0) + total_emission * rel_stake_parent_0) * I96F32::from_num(5)) - .saturating_div(I96F32::from_num(12)); + / I96F32::from_num(12); assert!( (I96F32::from_num(stake_parent_2) - expected_parent_stake).abs() / expected_parent_stake diff --git a/pallets/subtensor/src/tests/epoch.rs b/pallets/subtensor/src/tests/epoch.rs index ae0c8f669..a353ee5b9 100644 --- a/pallets/subtensor/src/tests/epoch.rs +++ b/pallets/subtensor/src/tests/epoch.rs @@ -2285,22 +2285,19 @@ fn test_compute_alpha_values() { // exp_val = exp(0.0 - 1.0 * 0.1) = exp(-0.1) // alpha[0] = 1 / (1 + exp(-0.1)) ~ 0.9048374180359595 let exp_val_0 = I32F32::from_num(0.9048374180359595); - let expected_alpha_0 = - I32F32::from_num(1.0).saturating_div(I32F32::from_num(1.0).saturating_add(exp_val_0)); + let expected_alpha_0 = I32F32::from_num(1.0) / I32F32::from_num(1.0).saturating_add(exp_val_0); // For consensus[1] = 0.5: // exp_val = exp(0.0 - 1.0 * 0.5) = exp(-0.5) // alpha[1] = 1 / (1 + exp(-0.5)) ~ 0.6065306597126334 let exp_val_1 = I32F32::from_num(0.6065306597126334); - let expected_alpha_1 = - I32F32::from_num(1.0).saturating_div(I32F32::from_num(1.0).saturating_add(exp_val_1)); + let expected_alpha_1 = I32F32::from_num(1.0) / I32F32::from_num(1.0).saturating_add(exp_val_1); // For consensus[2] = 0.9: // exp_val = exp(0.0 - 1.0 * 0.9) = exp(-0.9) // alpha[2] = 1 / (1 + exp(-0.9)) ~ 0.4065696597405991 let exp_val_2 = I32F32::from_num(0.4065696597405991); - let expected_alpha_2 = - I32F32::from_num(1.0).saturating_div(I32F32::from_num(1.0).saturating_add(exp_val_2)); + let expected_alpha_2 = I32F32::from_num(1.0) / I32F32::from_num(1.0).saturating_add(exp_val_2); // Define an epsilon for approximate equality checks. let epsilon = I32F32::from_num(1e-6); @@ -2338,8 +2335,7 @@ fn test_compute_alpha_values_256_miners() { let exp_val = safe_exp(exponent); // Use saturating addition and division - let expected_alpha = - I32F32::from_num(1.0).saturating_div(I32F32::from_num(1.0).saturating_add(exp_val)); + let expected_alpha = I32F32::from_num(1.0) / I32F32::from_num(1.0).saturating_add(exp_val); // Assert that the computed alpha values match the expected values within the epsilon. assert_approx_eq(alpha[i], expected_alpha, epsilon); diff --git a/pallets/subtensor/src/utils/misc.rs b/pallets/subtensor/src/utils/misc.rs index f1e0c7eb4..e211a2485 100644 --- a/pallets/subtensor/src/utils/misc.rs +++ b/pallets/subtensor/src/utils/misc.rs @@ -1,5 +1,6 @@ use super::*; use crate::{ + epoch::math::*, system::{ensure_root, ensure_signed_or_root, pallet_prelude::BlockNumberFor}, Error, }; @@ -592,7 +593,7 @@ impl<T: Config> Pallet<T> { SubnetOwnerCut::<T>::get() } pub fn get_float_subnet_owner_cut() -> I96F32 { - I96F32::from_num(SubnetOwnerCut::<T>::get()).saturating_div(I96F32::from_num(u16::MAX)) + I96F32::from_num(SubnetOwnerCut::<T>::get()).safe_div(I96F32::from_num(u16::MAX)) } pub fn set_subnet_owner_cut(subnet_owner_cut: u16) { SubnetOwnerCut::<T>::set(subnet_owner_cut); @@ -664,9 +665,8 @@ impl<T: Config> Pallet<T> { pub fn get_alpha_values_32(netuid: u16) -> (I32F32, I32F32) { let (alpha_low, alpha_high): (u16, u16) = AlphaValues::<T>::get(netuid); - let converted_low = I32F32::from_num(alpha_low).saturating_div(I32F32::from_num(u16::MAX)); - let converted_high = - I32F32::from_num(alpha_high).saturating_div(I32F32::from_num(u16::MAX)); + let converted_low = I32F32::from_num(alpha_low).safe_div(I32F32::from_num(u16::MAX)); + let converted_high = I32F32::from_num(alpha_high).safe_div(I32F32::from_num(u16::MAX)); (converted_low, converted_high) } diff --git a/runtime/src/lib.rs b/runtime/src/lib.rs index f8491dd57..372c9a981 100644 --- a/runtime/src/lib.rs +++ b/runtime/src/lib.rs @@ -1179,7 +1179,9 @@ const BLOCK_GAS_LIMIT: u64 = 75_000_000; /// `WeightPerGas` is an approximate ratio of the amount of Weight per Gas. /// fn weight_per_gas() -> Weight { - (NORMAL_DISPATCH_RATIO * MAXIMUM_BLOCK_WEIGHT).saturating_div(BLOCK_GAS_LIMIT) + (NORMAL_DISPATCH_RATIO * MAXIMUM_BLOCK_WEIGHT) + .checked_div(BLOCK_GAS_LIMIT) + .unwrap_or_default() } parameter_types! { From 445fae0a21592b22a03500f3cb6f62bd01b11351 Mon Sep 17 00:00:00 2001 From: Cameron Fairchild <cameron@opentensor.ai> Date: Mon, 27 Jan 2025 12:23:58 -0500 Subject: [PATCH 088/145] chore: clippy --- pallets/subtensor/src/tests/registration.rs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/pallets/subtensor/src/tests/registration.rs b/pallets/subtensor/src/tests/registration.rs index fa7c06f9e..768d923d0 100644 --- a/pallets/subtensor/src/tests/registration.rs +++ b/pallets/subtensor/src/tests/registration.rs @@ -526,10 +526,7 @@ fn test_burn_registration_doesnt_write_on_failure() { // Make sure the neuron is not registered. assert_eq!(SubtensorModule::get_subnetwork_n(netuid), 0); // Make sure the hotkey is not registered. - assert_eq!( - SubtensorModule::get_uid_for_net_and_hotkey(netuid, &hotkey_account_id).is_err(), - true - ); + assert!(SubtensorModule::get_uid_for_net_and_hotkey(netuid, &hotkey_account_id).is_err()); }); } From 88ab78c5e02622e0d84dbb7005a176501d71c1d8 Mon Sep 17 00:00:00 2001 From: Cameron Fairchild <cameron@opentensor.ai> Date: Mon, 27 Jan 2025 12:26:43 -0500 Subject: [PATCH 089/145] use option for identity instead --- pallets/subtensor/src/rpc_info/metagraph.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/pallets/subtensor/src/rpc_info/metagraph.rs b/pallets/subtensor/src/rpc_info/metagraph.rs index 091c7e259..1e77e5fe6 100644 --- a/pallets/subtensor/src/rpc_info/metagraph.rs +++ b/pallets/subtensor/src/rpc_info/metagraph.rs @@ -6,7 +6,7 @@ use frame_support::pallet_prelude::{Decode, Encode}; use substrate_fixed::types::I64F64; use subtensor_macros::freeze_struct; -#[freeze_struct("eff674535ea437ae")] +#[freeze_struct("bce2310daa502e48")] #[derive(Decode, Encode, PartialEq, Eq, Clone, Debug)] pub struct Metagraph<T: Config> { // Subnet index @@ -82,7 +82,7 @@ pub struct Metagraph<T: Config> { // Metagraph info. hotkeys: Vec<T::AccountId>, // hotkey per UID coldkeys: Vec<T::AccountId>, // coldkey per UID - identities: Vec<ChainIdentityOf>, // coldkeys identities + identities: Vec<Option<ChainIdentityOf>>, // coldkeys identities axons: Vec<AxonInfo>, // UID axons. active: Vec<bool>, // Avtive per UID validator_permit: Vec<bool>, // Val permit per UID @@ -114,7 +114,7 @@ impl<T: Config> Pallet<T> { let mut hotkeys: Vec<T::AccountId> = vec![]; let mut coldkeys: Vec<T::AccountId> = vec![]; let mut block_at_registration: Vec<Compact<u64>> = vec![]; - let mut identities: Vec<ChainIdentityOf> = vec![]; + let mut identities: Vec<Option<ChainIdentityOf>> = vec![]; let mut axons: Vec<AxonInfo> = vec![]; for uid in 0..n { let hotkey = Keys::<T>::get(netuid, uid); @@ -122,7 +122,7 @@ impl<T: Config> Pallet<T> { hotkeys.push(hotkey.clone()); coldkeys.push(coldkey.clone()); block_at_registration.push(BlockAtRegistration::<T>::get(netuid, uid).into()); - identities.push(Identities::<T>::get(coldkey.clone())?); + identities.push(Identities::<T>::get(coldkey.clone())); axons.push(Self::get_axon_info(netuid, &hotkey)); } let mut tao_dividends_per_hotkey: Vec<(T::AccountId, Compact<u64>)> = vec![]; From 5122fa7ec5c76b700df72e639e0106631bd516de Mon Sep 17 00:00:00 2001 From: Cameron Fairchild <cameron@opentensor.ai> Date: Mon, 27 Jan 2025 12:43:19 -0500 Subject: [PATCH 090/145] wait rate limit in tests --- pallets/subtensor/src/tests/swap_hotkey.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/pallets/subtensor/src/tests/swap_hotkey.rs b/pallets/subtensor/src/tests/swap_hotkey.rs index 528ad4576..8cf04d285 100644 --- a/pallets/subtensor/src/tests/swap_hotkey.rs +++ b/pallets/subtensor/src/tests/swap_hotkey.rs @@ -1264,6 +1264,8 @@ fn test_swap_parent_hotkey_childkey_maps() { // Set child and verify state maps mock_set_children(&coldkey, &parent_old, netuid, &[(u64::MAX, child)]); + // Wait rate limit + step_rate_limit(&TransactionType::SetChildren, netuid); // Schedule some pending child keys. mock_schedule_children(&coldkey, &parent_old, netuid, &[(u64::MAX, child_other)]); @@ -1317,6 +1319,8 @@ fn test_swap_child_hotkey_childkey_maps() { // Set child and verify state maps mock_set_children(&coldkey, &parent, netuid, &[(u64::MAX, child_old)]); + // Wait rate limit + step_rate_limit(&TransactionType::SetChildren, netuid); // Schedule some pending child keys. mock_schedule_children(&coldkey, &parent, netuid, &[(u64::MAX, child_old)]); From f81608d55a24323f5d1d2b4ecdde3dffd40db9c1 Mon Sep 17 00:00:00 2001 From: Cameron Fairchild <cameron@opentensor.ai> Date: Mon, 27 Jan 2025 13:13:25 -0500 Subject: [PATCH 091/145] use rem root alpha --- pallets/subtensor/src/coinbase/run_coinbase.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pallets/subtensor/src/coinbase/run_coinbase.rs b/pallets/subtensor/src/coinbase/run_coinbase.rs index 2e9fe44b4..d1e17f5e5 100644 --- a/pallets/subtensor/src/coinbase/run_coinbase.rs +++ b/pallets/subtensor/src/coinbase/run_coinbase.rs @@ -446,12 +446,12 @@ impl<T: Config> Pallet<T> { // 2.5.1 --- Store the root divs under hotkey_j root_alpha_divs .entry(hotkey_j.clone()) - .and_modify(|e| *e = e.saturating_add(root_alpha_divs_j.to_num::<u64>())) - .or_insert(root_alpha_divs_j.to_num::<u64>()); + .and_modify(|e| *e = e.saturating_add(rem_root_alpha_divs_j.to_num::<u64>())) + .or_insert(rem_root_alpha_divs_j.to_num::<u64>()); log::debug!( "Stored root alpha dividends for hotkey {:?}: {:?}", hotkey_j, - root_alpha_divs_j.to_num::<u64>() + rem_root_alpha_divs_j.to_num::<u64>() ); // 2.5.2 --- Store the alpha dividends From 13bd30b1cec464d57cdca7f5fcf359ff1a48676e Mon Sep 17 00:00:00 2001 From: Cameron Fairchild <cameron@opentensor.ai> Date: Mon, 27 Jan 2025 13:45:12 -0500 Subject: [PATCH 092/145] fix test involving alpha out emission --- pallets/subtensor/src/tests/coinbase.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pallets/subtensor/src/tests/coinbase.rs b/pallets/subtensor/src/tests/coinbase.rs index f62f23e86..08f1eb3b3 100644 --- a/pallets/subtensor/src/tests/coinbase.rs +++ b/pallets/subtensor/src/tests/coinbase.rs @@ -39,7 +39,7 @@ fn test_dynamic_function_various_values() { assert!(alpha_in_emission <= alpha_emission, "alpha_in_emission is greater than alpha_emission"); assert!(alpha_out_emission <= 2 * alpha_emission, "alpha_out_emission is greater than 2 * alpha_emission"); assert!((alpha_in_emission + alpha_out_emission) <= 2 * alpha_emission, "Sum of alpha_in_emission and alpha_out_emission is less than or equal to. 2 * alpha_emission"); - close( alpha_in_emission + alpha_out_emission, 2 * alpha_emission, 10 ); + close( alpha_in_emission + alpha_out_emission, alpha_in_emission + alpha_emission, 10 ); if alpha_in_emission > 0 || tao_in_emission > 0 { assert!((tao_in_emission as f64 / alpha_in_emission as f64 - price).abs() < 1e-1, "Ratio of tao_in_emission to alpha_in_emission is not equal to price"); } From f7e9566ced61f0abe4e0ae428285556b8cb7b436 Mon Sep 17 00:00:00 2001 From: Cameron Fairchild <cameron@opentensor.ai> Date: Mon, 27 Jan 2025 13:49:51 -0500 Subject: [PATCH 093/145] fix alpha out expectation --- pallets/subtensor/src/tests/coinbase.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pallets/subtensor/src/tests/coinbase.rs b/pallets/subtensor/src/tests/coinbase.rs index 08f1eb3b3..ff4908172 100644 --- a/pallets/subtensor/src/tests/coinbase.rs +++ b/pallets/subtensor/src/tests/coinbase.rs @@ -70,7 +70,7 @@ fn test_dynamic_function_price_equal_emission() { let expected_alpha_in: u64 = (alpha_block_emission * tao_subnet_emission) / tao_block_emission; close(alpha_in, expected_alpha_in, 10); - close(alpha_out, 2 * alpha_block_emission - expected_alpha_in, 10); + close(alpha_out, alpha_block_emission, 10); }); } From 33c6af0cee87653ceb54fbd26f421a4c387eb652 Mon Sep 17 00:00:00 2001 From: Greg Zaitsev <gregz@opentensor.dev> Date: Mon, 27 Jan 2025 14:28:10 -0500 Subject: [PATCH 094/145] Ban to_num, organize safe math into a crate --- Cargo.lock | 11 +++ pallets/subtensor/Cargo.toml | 1 + .../subtensor/src/coinbase/block_emission.rs | 12 ++-- pallets/subtensor/src/coinbase/block_step.rs | 6 +- pallets/subtensor/src/coinbase/root.rs | 2 +- .../subtensor/src/coinbase/run_coinbase.rs | 45 ++++++------ pallets/subtensor/src/epoch/math.rs | 66 ++++------------- pallets/subtensor/src/epoch/run_epoch.rs | 13 ++-- .../subtensor/src/rpc_info/delegate_info.rs | 6 +- pallets/subtensor/src/staking/helpers.rs | 4 +- pallets/subtensor/src/staking/move_stake.rs | 2 +- pallets/subtensor/src/staking/stake_utils.rs | 26 +++---- pallets/subtensor/src/subnets/weights.rs | 1 + pallets/subtensor/src/tests/children.rs | 2 +- pallets/subtensor/src/tests/delegate_info.rs | 2 +- pallets/subtensor/src/tests/math.rs | 20 +++--- pallets/subtensor/src/utils/misc.rs | 2 +- primitives/safe-math/Cargo.toml | 19 +++++ primitives/safe-math/src/lib.rs | 71 +++++++++++++++++++ primitives/share-pool/Cargo.toml | 1 + primitives/share-pool/src/lib.rs | 2 +- 21 files changed, 193 insertions(+), 121 deletions(-) create mode 100644 primitives/safe-math/Cargo.toml create mode 100644 primitives/safe-math/src/lib.rs diff --git a/Cargo.lock b/Cargo.lock index e86ef3d69..1d5fad7da 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -6456,6 +6456,7 @@ dependencies = [ "parity-util-mem", "rand", "rand_chacha", + "safe-math", "scale-info", "serde", "serde-tuple-vec-map", @@ -8032,6 +8033,15 @@ version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" +[[package]] +name = "safe-math" +version = "0.1.0" +dependencies = [ + "num-traits", + "sp-std 14.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-stable2409)", + "substrate-fixed", +] + [[package]] name = "safe-mix" version = "1.0.1" @@ -9568,6 +9578,7 @@ dependencies = [ name = "share-pool" version = "0.1.0" dependencies = [ + "safe-math", "sp-std 14.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-stable2409)", "substrate-fixed", ] diff --git a/pallets/subtensor/Cargo.toml b/pallets/subtensor/Cargo.toml index fe1bb7e2d..e8085615d 100644 --- a/pallets/subtensor/Cargo.toml +++ b/pallets/subtensor/Cargo.toml @@ -41,6 +41,7 @@ pallet-utility = { workspace = true } ndarray = { workspace = true } hex = { workspace = true } share-pool = { default-features = false, path = "../../primitives/share-pool" } +safe-math = { default-features = false, path = "../../primitives/safe-math" } approx = { workspace = true } pallet-collective = { version = "4.0.0-dev", default-features = false, path = "../collective" } diff --git a/pallets/subtensor/src/coinbase/block_emission.rs b/pallets/subtensor/src/coinbase/block_emission.rs index dbc93cf93..9ea99120d 100644 --- a/pallets/subtensor/src/coinbase/block_emission.rs +++ b/pallets/subtensor/src/coinbase/block_emission.rs @@ -1,6 +1,6 @@ use super::*; -use crate::epoch::math::*; use frame_support::traits::Get; +use safe_math::*; use substrate_fixed::{transcendental::log2, types::I96F32}; impl<T: Config> Pallet<T> { @@ -81,9 +81,9 @@ impl<T: Config> Pallet<T> { // Return result. ( - tao_in_emission.to_num::<u64>(), - alpha_in_emission.to_num::<u64>(), - alpha_out_emission.to_num::<u64>(), + tao_in_emission.saturating_to_num::<u64>(), + alpha_in_emission.saturating_to_num::<u64>(), + alpha_out_emission.saturating_to_num::<u64>(), ) } @@ -134,7 +134,7 @@ impl<T: Config> Pallet<T> { let floored_residual: I96F32 = residual.floor(); // Calculate the final emission rate using the floored residual. // Convert floored_residual to an integer - let floored_residual_int: u64 = floored_residual.to_num::<u64>(); + let floored_residual_int: u64 = floored_residual.saturating_to_num::<u64>(); // Multiply 2.0 by itself floored_residual times to calculate the power of 2. let mut multiplier: I96F32 = I96F32::from_num(1.0); for _ in 0..floored_residual_int { @@ -145,7 +145,7 @@ impl<T: Config> Pallet<T> { let block_emission: I96F32 = block_emission_percentage .saturating_mul(I96F32::from_num(DefaultBlockEmission::<T>::get())); // Convert to u64 - let block_emission_u64: u64 = block_emission.to_num::<u64>(); + let block_emission_u64: u64 = block_emission.saturating_to_num::<u64>(); if BlockEmission::<T>::get() != block_emission_u64 { BlockEmission::<T>::put(block_emission_u64); } diff --git a/pallets/subtensor/src/coinbase/block_step.rs b/pallets/subtensor/src/coinbase/block_step.rs index ce793def9..b8b27ae73 100644 --- a/pallets/subtensor/src/coinbase/block_step.rs +++ b/pallets/subtensor/src/coinbase/block_step.rs @@ -1,6 +1,6 @@ use super::*; -use crate::epoch::math::*; use frame_support::storage::IterableStorageMap; +use safe_math::*; use substrate_fixed::types::{I110F18, I96F32}; impl<T: Config + pallet_drand::Config> Pallet<T> { @@ -220,7 +220,7 @@ impl<T: Config + pallet_drand::Config> Pallet<T> { } else if next_value <= I110F18::from_num(Self::get_min_difficulty(netuid)) { return Self::get_min_difficulty(netuid); } else { - return next_value.to_num::<u64>(); + return next_value.saturating_to_num::<u64>(); } } @@ -254,7 +254,7 @@ impl<T: Config + pallet_drand::Config> Pallet<T> { } else if next_value <= I110F18::from_num(Self::get_min_burn_as_u64(netuid)) { return Self::get_min_burn_as_u64(netuid); } else { - return next_value.to_num::<u64>(); + return next_value.saturating_to_num::<u64>(); } } } diff --git a/pallets/subtensor/src/coinbase/root.rs b/pallets/subtensor/src/coinbase/root.rs index 165526ac2..9e510fa0a 100644 --- a/pallets/subtensor/src/coinbase/root.rs +++ b/pallets/subtensor/src/coinbase/root.rs @@ -16,10 +16,10 @@ // DEALINGS IN THE SOFTWARE. use super::*; -use crate::epoch::math::*; use frame_support::dispatch::Pays; use frame_support::storage::IterableStorageDoubleMap; use frame_support::weights::Weight; +use safe_math::*; use sp_core::Get; use sp_std::vec; use substrate_fixed::types::I64F64; diff --git a/pallets/subtensor/src/coinbase/run_coinbase.rs b/pallets/subtensor/src/coinbase/run_coinbase.rs index e15dc15d3..cc15d7cbe 100644 --- a/pallets/subtensor/src/coinbase/run_coinbase.rs +++ b/pallets/subtensor/src/coinbase/run_coinbase.rs @@ -1,6 +1,6 @@ use super::*; -use crate::epoch::math::*; use alloc::collections::BTreeMap; +use safe_math::*; use substrate_fixed::types::I96F32; use tle::stream_ciphers::AESGCMStreamCipherProvider; use tle::tlock::tld; @@ -118,7 +118,7 @@ impl<T: Config> Pallet<T> { let tao_in: u64 = mech_emission .checked_mul(subnet_proportion) .unwrap_or(I96F32::from_num(0)) - .to_num::<u64>(); + .saturating_to_num::<u64>(); log::debug!( "Subnet TAO emission (E_s) for netuid {:?}: {:?}", netuid, @@ -201,7 +201,7 @@ impl<T: Config> Pallet<T> { // Calculate the owner cut. let owner_cut: u64 = I96F32::from_num(alpha_out_emission) .saturating_mul(Self::get_float_subnet_owner_cut()) - .to_num::<u64>(); + .saturating_to_num::<u64>(); log::debug!("Owner cut for netuid {:?}: {:?}", netuid, owner_cut); // Store the owner cut for this subnet. *owner_cuts.entry(*netuid).or_insert(0) = owner_cut; @@ -225,20 +225,25 @@ impl<T: Config> Pallet<T> { let pending_alpha_emission: I96F32 = I96F32::from_num(remaining_emission).saturating_sub(root_emission_in_alpha); // Sell root emission through the pool. - let root_emission_in_tao: u64 = - Self::swap_alpha_for_tao(*netuid, root_emission_in_alpha.to_num::<u64>()); - SubnetAlphaEmissionSell::<T>::insert(*netuid, root_emission_in_alpha.to_num::<u64>()); + let root_emission_in_tao: u64 = Self::swap_alpha_for_tao( + *netuid, + root_emission_in_alpha.saturating_to_num::<u64>(), + ); + SubnetAlphaEmissionSell::<T>::insert( + *netuid, + root_emission_in_alpha.saturating_to_num::<u64>(), + ); // Accumulate root divs for subnet. PendingRootDivs::<T>::mutate(*netuid, |total| { *total = total.saturating_add(root_emission_in_tao); }); // Accumulate alpha that was swapped for the pending root divs. PendingAlphaSwapped::<T>::mutate(*netuid, |total| { - *total = total.saturating_add(root_emission_in_alpha.to_num::<u64>()); + *total = total.saturating_add(root_emission_in_alpha.saturating_to_num::<u64>()); }); // Accumulate alpha emission in pending. PendingEmission::<T>::mutate(*netuid, |total| { - *total = total.saturating_add(pending_alpha_emission.to_num::<u64>()); + *total = total.saturating_add(pending_alpha_emission.saturating_to_num::<u64>()); }); // Accumulate the owner cut in pending. PendingOwnerCut::<T>::mutate(*netuid, |total| { @@ -413,14 +418,14 @@ impl<T: Config> Pallet<T> { // Store the root-alpha divs under hotkey_j root_alpha_divs .entry(hotkey_j.clone()) - .and_modify(|e| *e = e.saturating_add(root_divs.to_num::<u64>())) - .or_insert(root_divs.to_num::<u64>()); + .and_modify(|e| *e = e.saturating_add(root_divs.saturating_to_num::<u64>())) + .or_insert(root_divs.saturating_to_num::<u64>()); total_root_alpha_divs = - total_root_alpha_divs.saturating_add(root_divs.to_num::<u64>()); + total_root_alpha_divs.saturating_add(root_divs.saturating_to_num::<u64>()); log::debug!( "Stored root alpha dividends for hotkey {:?}: {:?}", hotkey_j, - root_divs.to_num::<u64>() + root_divs.saturating_to_num::<u64>() ); } } @@ -478,26 +483,26 @@ impl<T: Config> Pallet<T> { hotkey_j, &Owner::<T>::get(hotkey_j.clone()), netuid, - validator_take.to_num::<u64>(), + validator_take.saturating_to_num::<u64>(), ); log::debug!( "Distributed validator take for hotkey {:?} on netuid {:?}: {:?}", hotkey_j, netuid, - validator_take.to_num::<u64>() + validator_take.saturating_to_num::<u64>() ); // Distribute the alpha divs to the hotkey. Self::increase_stake_for_hotkey_on_subnet( hotkey_j, netuid, - rem_divs_j.to_num::<u64>(), + rem_divs_j.saturating_to_num::<u64>(), ); log::debug!( "Distributed alpha dividends for hotkey {:?} on netuid {:?}: {:?}", hotkey_j, netuid, - rem_divs_j.to_num::<u64>() + rem_divs_j.saturating_to_num::<u64>() ); // Record dividends for this hotkey on this subnet. @@ -522,7 +527,7 @@ impl<T: Config> Pallet<T> { .unwrap_or(I96F32::from_num(0)); let root_divs_to_pay: u64 = proportion .saturating_mul(I96F32::from_num(pending_root_divs)) - .to_num::<u64>(); + .saturating_to_num::<u64>(); log::debug!( "Proportion for hotkey {:?}: {:?}, root_divs_to_pay: {:?}", hotkey_j, @@ -581,7 +586,7 @@ impl<T: Config> Pallet<T> { let combined_contribution: I96F32 = alpha_contribution.saturating_add(root_contribution); // Return the combined contribution as a u64 - combined_contribution.to_num::<u64>() + combined_contribution.saturating_to_num::<u64>() } /// Returns a list of tuples for each parent associated with this hotkey including self @@ -695,7 +700,7 @@ impl<T: Config> Pallet<T> { .checked_div(total_contribution) .unwrap_or(I96F32::from_num(0)); let parent_emission: u64 = - (remaining_emission.saturating_mul(emission_factor)).to_num::<u64>(); + (remaining_emission.saturating_mul(emission_factor)).saturating_to_num::<u64>(); // Add the parent's emission to the distribution list dividend_tuples.push((parent, parent_emission)); @@ -707,7 +712,7 @@ impl<T: Config> Pallet<T> { // This includes the take left from the parents and the self contribution. let child_emission = remaining_emission .saturating_add(child_emission_take) - .to_num::<u64>() + .saturating_to_num::<u64>() .saturating_sub(to_parents); // Add the hotkey's own emission to the distribution list diff --git a/pallets/subtensor/src/epoch/math.rs b/pallets/subtensor/src/epoch/math.rs index 926375446..e57d63b3a 100644 --- a/pallets/subtensor/src/epoch/math.rs +++ b/pallets/subtensor/src/epoch/math.rs @@ -3,12 +3,13 @@ use crate::alloc::borrow::ToOwned; #[allow(unused)] use num_traits::float::Float; +use safe_math::*; use sp_runtime::traits::{CheckedAdd, Saturating}; use sp_std::cmp::Ordering; use sp_std::vec; use substrate_fixed::transcendental::{exp, ln}; -use substrate_fixed::types::{I110F18, I32F32, I64F64, I96F32, U64F64}; +use substrate_fixed::types::{I32F32, I64F64}; // TODO: figure out what cfg gate this needs to not be a warning in rustc #[allow(unused)] @@ -21,17 +22,17 @@ pub fn fixed(val: f32) -> I32F32 { #[allow(dead_code)] pub fn fixed_to_u16(x: I32F32) -> u16 { - x.to_num::<u16>() + x.saturating_to_num::<u16>() } #[allow(dead_code)] pub fn fixed_to_u64(x: I32F32) -> u64 { - x.to_num::<u64>() + x.saturating_to_num::<u64>() } #[allow(dead_code)] pub fn fixed64_to_u64(x: I64F64) -> u64 { - x.to_num::<u64>() + x.saturating_to_num::<u64>() } #[allow(dead_code)] @@ -100,7 +101,7 @@ pub fn vec_max_upscale_to_u16(vec: &[I32F32]) -> Vec<u16> { if *val == I32F32::from_num(0) { return vec .iter() - .map(|e: &I32F32| e.saturating_mul(u16_max).to_num::<u16>()) + .map(|e: &I32F32| e.saturating_mul(u16_max).saturating_to_num::<u16>()) .collect(); } if *val > threshold { @@ -109,7 +110,7 @@ pub fn vec_max_upscale_to_u16(vec: &[I32F32]) -> Vec<u16> { .map(|e: &I32F32| { e.saturating_mul(u16_max.safe_div(*val)) .round() - .to_num::<u16>() + .saturating_to_num::<u16>() }) .collect(); } @@ -118,14 +119,18 @@ pub fn vec_max_upscale_to_u16(vec: &[I32F32]) -> Vec<u16> { e.saturating_mul(u16_max) .safe_div(*val) .round() - .to_num::<u16>() + .saturating_to_num::<u16>() }) .collect() } None => { let sum: I32F32 = vec.iter().sum(); vec.iter() - .map(|e: &I32F32| e.saturating_mul(u16_max).safe_div(sum).to_num::<u16>()) + .map(|e: &I32F32| { + e.saturating_mul(u16_max) + .safe_div(sum) + .saturating_to_num::<u16>() + }) .collect() } } @@ -1406,48 +1411,3 @@ pub fn safe_ln(value: I32F32) -> I32F32 { pub fn safe_exp(value: I32F32) -> I32F32 { exp(value).unwrap_or(I32F32::from_num(0.0)) } - -/// Safe division trait -pub trait SafeDiv { - /// Safe division that returns supplied default value for division by zero - fn safe_div_or(self, rhs: Self, def: Self) -> Self; - /// Safe division that returns default value for division by zero - fn safe_div(self, rhs: Self) -> Self; -} - -/// Implementation of safe division trait for primitive types -macro_rules! impl_safe_div_for_primitive { - ($($t:ty),*) => { - $( - impl SafeDiv for $t { - fn safe_div_or(self, rhs: Self, def: Self) -> Self { - self.checked_div(rhs).unwrap_or(def) - } - - fn safe_div(self, rhs: Self) -> Self { - self.checked_div(rhs).unwrap_or_default() - } - } - )* - }; -} -impl_safe_div_for_primitive!(u8, u16, u32, u64, i8, i16, i32, i64, usize); - -/// Implementation of safe division trait for substrate fixed types -macro_rules! impl_safe_div_for_fixed { - ($($t:ty),*) => { - $( - impl SafeDiv for $t { - fn safe_div_or(self, rhs: Self, def: Self) -> Self { - self.checked_div(rhs).unwrap_or(def) - } - - fn safe_div(self, rhs: Self) -> Self { - self.checked_div(rhs).unwrap_or_default() - } - } - )* - }; -} - -impl_safe_div_for_fixed!(I96F32, I32F32, I64F64, I110F18, U64F64); diff --git a/pallets/subtensor/src/epoch/run_epoch.rs b/pallets/subtensor/src/epoch/run_epoch.rs index 9b3023818..4e9f36e4e 100644 --- a/pallets/subtensor/src/epoch/run_epoch.rs +++ b/pallets/subtensor/src/epoch/run_epoch.rs @@ -1,6 +1,7 @@ use super::*; use crate::epoch::math::*; use frame_support::IterableStorageDoubleMap; +use safe_math::*; use sp_std::vec; use substrate_fixed::types::{I32F32, I64F64, I96F32}; @@ -238,7 +239,7 @@ impl<T: Config> Pallet<T> { .collect(); let server_emission: Vec<u64> = server_emission .iter() - .map(|e: &I96F32| e.to_num::<u64>()) + .map(|e: &I96F32| e.saturating_to_num::<u64>()) .collect(); let validator_emission: Vec<I96F32> = normalized_validator_emission @@ -247,7 +248,7 @@ impl<T: Config> Pallet<T> { .collect(); let validator_emission: Vec<u64> = validator_emission .iter() - .map(|e: &I96F32| e.to_num::<u64>()) + .map(|e: &I96F32| e.saturating_to_num::<u64>()) .collect(); // Used only to track combined emission in the storage. @@ -257,7 +258,7 @@ impl<T: Config> Pallet<T> { .collect(); let combined_emission: Vec<u64> = combined_emission .iter() - .map(|e: &I96F32| e.to_num::<u64>()) + .map(|e: &I96F32| e.saturating_to_num::<u64>()) .collect(); log::trace!("nSE: {:?}", &normalized_server_emission); @@ -609,7 +610,7 @@ impl<T: Config> Pallet<T> { .collect(); let server_emission: Vec<u64> = server_emission .iter() - .map(|e: &I96F32| e.to_num::<u64>()) + .map(|e: &I96F32| e.saturating_to_num::<u64>()) .collect(); let validator_emission: Vec<I96F32> = normalized_validator_emission @@ -618,7 +619,7 @@ impl<T: Config> Pallet<T> { .collect(); let validator_emission: Vec<u64> = validator_emission .iter() - .map(|e: &I96F32| e.to_num::<u64>()) + .map(|e: &I96F32| e.saturating_to_num::<u64>()) .collect(); // Only used to track emission in storage. @@ -628,7 +629,7 @@ impl<T: Config> Pallet<T> { .collect(); let combined_emission: Vec<u64> = combined_emission .iter() - .map(|e: &I96F32| e.to_num::<u64>()) + .map(|e: &I96F32| e.saturating_to_num::<u64>()) .collect(); log::trace!( diff --git a/pallets/subtensor/src/rpc_info/delegate_info.rs b/pallets/subtensor/src/rpc_info/delegate_info.rs index ec8d48110..0f79ab2eb 100644 --- a/pallets/subtensor/src/rpc_info/delegate_info.rs +++ b/pallets/subtensor/src/rpc_info/delegate_info.rs @@ -1,8 +1,8 @@ use super::*; -use crate::epoch::math::*; use frame_support::pallet_prelude::{Decode, Encode}; use frame_support::storage::IterableStorageMap; use frame_support::IterableStorageDoubleMap; +use safe_math::*; use substrate_fixed::types::U64F64; extern crate alloc; use codec::Compact; @@ -102,8 +102,8 @@ impl<T: Config> Pallet<T> { owner_ss58: owner.clone(), registrations: registrations.iter().map(|x| x.into()).collect(), validator_permits, - return_per_1000: U64F64::to_num::<u64>(return_per_1000).into(), - total_daily_return: U64F64::to_num::<u64>(emissions_per_day).into(), + return_per_1000: return_per_1000.saturating_to_num::<u64>().into(), + total_daily_return: emissions_per_day.saturating_to_num::<u64>().into(), } } diff --git a/pallets/subtensor/src/staking/helpers.rs b/pallets/subtensor/src/staking/helpers.rs index 1a1a2d00d..5bb10e0bb 100644 --- a/pallets/subtensor/src/staking/helpers.rs +++ b/pallets/subtensor/src/staking/helpers.rs @@ -49,7 +49,7 @@ impl<T: Config> Pallet<T> { let alpha: I96F32 = I96F32::from_num(Self::get_stake_for_hotkey_on_subnet(hotkey, *netuid)); let tao_price: I96F32 = Self::get_alpha_price(*netuid); - alpha.saturating_mul(tao_price).to_num::<u64>() + alpha.saturating_mul(tao_price).saturating_to_num::<u64>() }) .sum() } @@ -67,7 +67,7 @@ impl<T: Config> Pallet<T> { total_stake = total_stake.saturating_add( I96F32::from_num(alpha) .saturating_mul(tao_price) - .to_num::<u64>(), + .saturating_to_num::<u64>(), ); } total_stake diff --git a/pallets/subtensor/src/staking/move_stake.rs b/pallets/subtensor/src/staking/move_stake.rs index 88dd1463f..75f933135 100644 --- a/pallets/subtensor/src/staking/move_stake.rs +++ b/pallets/subtensor/src/staking/move_stake.rs @@ -1,5 +1,5 @@ use super::*; -use crate::epoch::math::*; +use safe_math::*; use sp_core::Get; impl<T: Config> Pallet<T> { diff --git a/pallets/subtensor/src/staking/stake_utils.rs b/pallets/subtensor/src/staking/stake_utils.rs index bb30b5c34..482b7c163 100644 --- a/pallets/subtensor/src/staking/stake_utils.rs +++ b/pallets/subtensor/src/staking/stake_utils.rs @@ -1,5 +1,5 @@ use super::*; -use crate::epoch::math::*; +use safe_math::*; use share_pool::{SharePool, SharePoolDataOperations}; use sp_std::ops::Neg; use substrate_fixed::types::{I64F64, I96F32, U64F64}; @@ -208,7 +208,7 @@ impl<T: Config> Pallet<T> { initial_alpha ); if netuid == 0 { - return initial_alpha.to_num::<u64>(); + return initial_alpha.saturating_to_num::<u64>(); } // Initialize variables to track alpha allocated to children and inherited from parents. @@ -299,7 +299,7 @@ impl<T: Config> Pallet<T> { ); // Step 6: Return the final inherited alpha value. - finalized_alpha.to_num::<u64>() + finalized_alpha.saturating_to_num::<u64>() } /// Checks if a specific hotkey-coldkey pair has enough stake on a subnet to fulfill a given decrement. @@ -476,7 +476,7 @@ impl<T: Config> Pallet<T> { I96F32::from_num(tao) }; // Return simulated amount. - alpha.to_num::<u64>() + alpha.saturating_to_num::<u64>() } /// Swaps a subnet's Alpba token for TAO. @@ -502,7 +502,7 @@ impl<T: Config> Pallet<T> { // Step 3.b.1: Stable mechanism, just return the value 1:1 I96F32::from_num(alpha) }; - tao.to_num::<u64>() + tao.saturating_to_num::<u64>() } /// Swaps TAO for the alpha token on the subnet. @@ -530,11 +530,11 @@ impl<T: Config> Pallet<T> { }; // Step 4. Decrease Alpha reserves. SubnetAlphaIn::<T>::mutate(netuid, |total| { - *total = total.saturating_sub(alpha.to_num::<u64>()); + *total = total.saturating_sub(alpha.saturating_to_num::<u64>()); }); // Step 5: Increase Alpha outstanding. SubnetAlphaOut::<T>::mutate(netuid, |total| { - *total = total.saturating_add(alpha.to_num::<u64>()); + *total = total.saturating_add(alpha.saturating_to_num::<u64>()); }); // Step 6: Increase Tao reserves. SubnetTAO::<T>::mutate(netuid, |total| { @@ -549,7 +549,7 @@ impl<T: Config> Pallet<T> { *total = total.saturating_sub(tao); }); // Step 9. Return the alpha received. - alpha.to_num::<u64>() + alpha.saturating_to_num::<u64>() } /// Swaps a subnet's Alpba token for TAO. @@ -585,18 +585,18 @@ impl<T: Config> Pallet<T> { }); // Step 6: Decrease tao reserves. SubnetTAO::<T>::mutate(netuid, |total| { - *total = total.saturating_sub(tao.to_num::<u64>()); + *total = total.saturating_sub(tao.saturating_to_num::<u64>()); }); // Step 7: Reduce total TAO reserves. TotalStake::<T>::mutate(|total| { - *total = total.saturating_sub(tao.to_num::<u64>()); + *total = total.saturating_sub(tao.saturating_to_num::<u64>()); }); // Step 8. Decrease Alpha reserves. SubnetVolume::<T>::mutate(netuid, |total| { - *total = total.saturating_sub(tao.to_num::<u64>()); + *total = total.saturating_sub(tao.saturating_to_num::<u64>()); }); // Step 9. Return the tao received. - tao.to_num::<u64>() + tao.saturating_to_num::<u64>() } /// Unstakes alpha from a subnet for a given hotkey and coldkey pair. @@ -891,7 +891,7 @@ impl<T: Config> SharePoolDataOperations<AlphaShareKey<T>> crate::TotalHotkeyAlpha::<T>::insert( &(self.hotkey), self.netuid, - value.to_num::<u64>(), + value.saturating_to_num::<u64>(), ); } else { crate::TotalHotkeyAlpha::<T>::remove(&(self.hotkey), self.netuid); diff --git a/pallets/subtensor/src/subnets/weights.rs b/pallets/subtensor/src/subnets/weights.rs index 13fa0dafc..ad868c52d 100644 --- a/pallets/subtensor/src/subnets/weights.rs +++ b/pallets/subtensor/src/subnets/weights.rs @@ -1,6 +1,7 @@ use super::*; use crate::epoch::math::*; use codec::Compact; +use safe_math::*; use sp_core::{ConstU32, H256}; use sp_runtime::{ traits::{BlakeTwo256, Hash}, diff --git a/pallets/subtensor/src/tests/children.rs b/pallets/subtensor/src/tests/children.rs index 7a3780c0b..f10e75603 100644 --- a/pallets/subtensor/src/tests/children.rs +++ b/pallets/subtensor/src/tests/children.rs @@ -3357,7 +3357,7 @@ fn test_parent_child_chain_emission() { let total_tao: I96F32 = I96F32::from_num(300_000 + 100_000 + 50_000); let total_alpha: I96F32 = I96F32::from_num(SubtensorModule::swap_tao_for_alpha( netuid, - total_tao.saturating_to_num::<u64>(), + total_tao.to_num::<u64>(), )); // Set the stakes directly diff --git a/pallets/subtensor/src/tests/delegate_info.rs b/pallets/subtensor/src/tests/delegate_info.rs index 78ea48482..0dcb69d78 100644 --- a/pallets/subtensor/src/tests/delegate_info.rs +++ b/pallets/subtensor/src/tests/delegate_info.rs @@ -6,7 +6,7 @@ use super::mock::*; #[test] fn test_return_per_1000_tao() { let take = // 18% take to the Validator - Compact::<u16>::from((U64F64::from_num(0.18 * u16::MAX as f64)).saturating_to_num::<u16>()); + Compact::<u16>::from((U64F64::from_num(0.18 * u16::MAX as f64)).to_num::<u16>()); // 10_000 TAO total validator stake let total_stake = U64F64::from_num(10_000.0 * 1e9); diff --git a/pallets/subtensor/src/tests/math.rs b/pallets/subtensor/src/tests/math.rs index 84fcc4aac..7409ed8eb 100644 --- a/pallets/subtensor/src/tests/math.rs +++ b/pallets/subtensor/src/tests/math.rs @@ -2264,16 +2264,17 @@ fn test_math_fixed_to_u64() { } #[test] -#[should_panic(expected = "-1 overflows")] -fn test_math_fixed_to_u64_panics() { +fn test_math_fixed_to_u64_saturates() { let bad_input = I32F32::from_num(-1); - fixed_to_u64(bad_input); + let expected = 0; + assert_eq!(fixed_to_u64(bad_input), expected); } #[test] fn test_math_fixed64_to_u64() { let expected = u64::MIN; - assert_eq!(fixed64_to_u64(I64F64::from_num(expected)), expected); + let input = I64F64::from_num(expected); + assert_eq!(fixed64_to_u64(input), expected); let input = i64::MAX / 2; let expected = u64::try_from(input).unwrap(); @@ -2285,10 +2286,10 @@ fn test_math_fixed64_to_u64() { } #[test] -#[should_panic(expected = "-1 overflows")] -fn test_math_fixed64_to_u64_panics() { +fn test_math_fixed64_to_u64_saturates() { let bad_input = I64F64::from_num(-1); - fixed64_to_u64(bad_input); + let expected = 0; + assert_eq!(fixed64_to_u64(bad_input), expected); } /* @TODO: find the _true_ max, and half, input values */ @@ -2340,14 +2341,15 @@ fn test_fixed_proportion_to_u16() { } #[test] -#[should_panic(expected = "overflow")] -fn test_fixed_proportion_to_u16_panics() { +fn test_fixed_proportion_to_u16_saturates() { let expected = u16::MAX; let input = I32F32::from_num(expected); log::trace!("Testing with input: {:?}", input); // Debug output let result = fixed_proportion_to_u16(input); log::trace!("Testing with result: {:?}", result); // Debug output + assert_eq!(result, expected); } + #[test] fn test_vec_fixed64_to_fixed32() { let input = vec![I64F64::from_num(i32::MIN)]; diff --git a/pallets/subtensor/src/utils/misc.rs b/pallets/subtensor/src/utils/misc.rs index e211a2485..4749d6594 100644 --- a/pallets/subtensor/src/utils/misc.rs +++ b/pallets/subtensor/src/utils/misc.rs @@ -1,9 +1,9 @@ use super::*; use crate::{ - epoch::math::*, system::{ensure_root, ensure_signed_or_root, pallet_prelude::BlockNumberFor}, Error, }; +use safe_math::*; use sp_core::Get; use sp_core::U256; use sp_runtime::Saturating; diff --git a/primitives/safe-math/Cargo.toml b/primitives/safe-math/Cargo.toml new file mode 100644 index 000000000..f50ac6b90 --- /dev/null +++ b/primitives/safe-math/Cargo.toml @@ -0,0 +1,19 @@ +[package] +name = "safe-math" +version = "0.1.0" +edition = "2021" + +[dependencies] +substrate-fixed = { workspace = true } +sp-std = { workspace = true } +num-traits = { version = "0.2.19", default-features = false, features = ["libm"] } + +[lints] +workspace = true + +[features] +default = ["std"] +std = [ + "substrate-fixed/std", + "sp-std/std", +] diff --git a/primitives/safe-math/src/lib.rs b/primitives/safe-math/src/lib.rs new file mode 100644 index 000000000..83c27b07c --- /dev/null +++ b/primitives/safe-math/src/lib.rs @@ -0,0 +1,71 @@ +#![cfg_attr(not(feature = "std"), no_std)] +#![allow(clippy::result_unit_err)] + +use substrate_fixed::types::{I110F18, I32F32, I64F64, I96F32, U64F64}; + +/// Safe division trait +pub trait SafeDiv { + /// Safe division that returns supplied default value for division by zero + fn safe_div_or(self, rhs: Self, def: Self) -> Self; + /// Safe division that returns default value for division by zero + fn safe_div(self, rhs: Self) -> Self; +} + +/// Implementation of safe division trait for primitive types +macro_rules! impl_safe_div_for_primitive { + ($($t:ty),*) => { + $( + impl SafeDiv for $t { + fn safe_div_or(self, rhs: Self, def: Self) -> Self { + self.checked_div(rhs).unwrap_or(def) + } + + fn safe_div(self, rhs: Self) -> Self { + self.checked_div(rhs).unwrap_or_default() + } + } + )* + }; +} +impl_safe_div_for_primitive!(u8, u16, u32, u64, i8, i16, i32, i64, usize); + +/// Implementation of safe division trait for substrate fixed types +macro_rules! impl_safe_div_for_fixed { + ($($t:ty),*) => { + $( + impl SafeDiv for $t { + fn safe_div_or(self, rhs: Self, def: Self) -> Self { + self.checked_div(rhs).unwrap_or(def) + } + + fn safe_div(self, rhs: Self) -> Self { + self.checked_div(rhs).unwrap_or_default() + } + } + )* + }; +} +impl_safe_div_for_fixed!(I96F32, I32F32, I64F64, I110F18, U64F64); + +// /// Trait for safe conversion to primitive type P +// pub trait SafeToNum<T> { +// /// Safe conversion to primitive type P +// fn safe_to_num<P>(self) -> P +// where +// P: num_traits::Bounded + substrate_fixed::prelude::ToFixed + substrate_fixed::prelude::FromFixed; +// } + +// impl<T> SafeToNum<T> for T +// where +// T: substrate_fixed::traits::Fixed, +// { +// fn safe_to_num<P>(self) -> P +// where +// P: num_traits::Bounded + substrate_fixed::prelude::ToFixed + substrate_fixed::prelude::FromFixed +// { +// match self.try_into() { +// Ok(value) => value, +// Err(_) => P::max_value(), +// } +// } +// } diff --git a/primitives/share-pool/Cargo.toml b/primitives/share-pool/Cargo.toml index 219123269..370f32cbb 100644 --- a/primitives/share-pool/Cargo.toml +++ b/primitives/share-pool/Cargo.toml @@ -6,6 +6,7 @@ edition = "2021" [dependencies] substrate-fixed = { workspace = true } sp-std = { workspace = true } +safe-math = { default-features = false, path = "../safe-math" } [lints] workspace = true diff --git a/primitives/share-pool/src/lib.rs b/primitives/share-pool/src/lib.rs index 2f4d25ef9..8af506945 100644 --- a/primitives/share-pool/src/lib.rs +++ b/primitives/share-pool/src/lib.rs @@ -54,7 +54,7 @@ where .checked_div(denominator) .unwrap_or(U64F64::from_num(0)) .saturating_mul(current_share) - .to_num::<u64>() + .saturating_to_num::<u64>() } pub fn try_get_value(&self, key: &K) -> Result<u64, ()> { From 46f02950142f469bba8c69fbf40bf53a7133cb95 Mon Sep 17 00:00:00 2001 From: Greg Zaitsev <gregz@opentensor.dev> Date: Mon, 27 Jan 2025 14:46:39 -0500 Subject: [PATCH 095/145] Avoid using from_num --- .../subtensor/src/coinbase/block_emission.rs | 40 ++-- pallets/subtensor/src/coinbase/block_step.rs | 39 ++-- pallets/subtensor/src/coinbase/root.rs | 4 +- .../subtensor/src/coinbase/run_coinbase.rs | 131 +++++++------ pallets/subtensor/src/epoch/math.rs | 175 ++++++++++-------- pallets/subtensor/src/epoch/run_epoch.rs | 79 ++++---- pallets/subtensor/src/lib.rs | 2 +- pallets/subtensor/src/macros/genesis.rs | 4 +- .../subtensor/src/migrations/migrate_rao.rs | 4 +- .../subtensor/src/rpc_info/delegate_info.rs | 16 +- pallets/subtensor/src/staking/helpers.rs | 7 +- pallets/subtensor/src/staking/stake_utils.rs | 103 ++++++----- pallets/subtensor/src/tests/math.rs | 10 +- pallets/subtensor/src/utils/misc.rs | 9 +- primitives/share-pool/src/lib.rs | 64 ++++--- 15 files changed, 377 insertions(+), 310 deletions(-) diff --git a/pallets/subtensor/src/coinbase/block_emission.rs b/pallets/subtensor/src/coinbase/block_emission.rs index 9ea99120d..af9f8dd51 100644 --- a/pallets/subtensor/src/coinbase/block_emission.rs +++ b/pallets/subtensor/src/coinbase/block_emission.rs @@ -31,15 +31,15 @@ impl<T: Config> Pallet<T> { alpha_block_emission: u64, ) -> (u64, u64, u64) { // Init terms. - let mut tao_in_emission: I96F32 = I96F32::from_num(tao_emission); - let float_alpha_block_emission: I96F32 = I96F32::from_num(alpha_block_emission); + let mut tao_in_emission: I96F32 = I96F32::saturating_from_num(tao_emission); + let float_alpha_block_emission: I96F32 = I96F32::saturating_from_num(alpha_block_emission); // Get alpha price for subnet. let alpha_price: I96F32 = Self::get_alpha_price(netuid); log::debug!("{:?} - alpha_price: {:?}", netuid, alpha_price); // Get initial alpha_in - let mut alpha_in_emission: I96F32 = I96F32::from_num(tao_emission) + let mut alpha_in_emission: I96F32 = I96F32::saturating_from_num(tao_emission) .checked_div(alpha_price) .unwrap_or(float_alpha_block_emission); @@ -60,13 +60,15 @@ impl<T: Config> Pallet<T> { } // Avoid rounding errors. - if tao_in_emission < I96F32::from_num(1) || alpha_in_emission < I96F32::from_num(1) { - alpha_in_emission = I96F32::from_num(0); - tao_in_emission = I96F32::from_num(0); + if tao_in_emission < I96F32::saturating_from_num(1) + || alpha_in_emission < I96F32::saturating_from_num(1) + { + alpha_in_emission = I96F32::saturating_from_num(0); + tao_in_emission = I96F32::saturating_from_num(0); } // Set Alpha in emission. - let alpha_out_emission = I96F32::from_num(2) + let alpha_out_emission = I96F32::saturating_from_num(2) .saturating_mul(float_alpha_block_emission) .saturating_sub(alpha_in_emission); @@ -106,23 +108,22 @@ impl<T: Config> Pallet<T> { /// Returns the block emission for an issuance value. pub fn get_block_emission_for_issuance(issuance: u64) -> Result<u64, &'static str> { // Convert issuance to a float for calculations below. - let total_issuance: I96F32 = I96F32::from_num(issuance); + let total_issuance: I96F32 = I96F32::saturating_from_num(issuance); // Check to prevent division by zero when the total supply is reached // and creating an issuance greater than the total supply. - if total_issuance >= I96F32::from_num(TotalSupply::<T>::get()) { + if total_issuance >= I96F32::saturating_from_num(TotalSupply::<T>::get()) { return Ok(0); } // Calculate the logarithmic residual of the issuance against half the total supply. let residual: I96F32 = log2( - I96F32::from_num(1.0) + I96F32::saturating_from_num(1.0) .checked_div( - I96F32::from_num(1.0) + I96F32::saturating_from_num(1.0) .checked_sub( total_issuance - .checked_div( - I96F32::from_num(2.0) - .saturating_mul(I96F32::from_num(10_500_000_000_000_000.0)), - ) + .checked_div(I96F32::saturating_from_num(2.0).saturating_mul( + I96F32::saturating_from_num(10_500_000_000_000_000.0), + )) .ok_or("Logarithm calculation failed")?, ) .ok_or("Logarithm calculation failed")?, @@ -136,14 +137,15 @@ impl<T: Config> Pallet<T> { // Convert floored_residual to an integer let floored_residual_int: u64 = floored_residual.saturating_to_num::<u64>(); // Multiply 2.0 by itself floored_residual times to calculate the power of 2. - let mut multiplier: I96F32 = I96F32::from_num(1.0); + let mut multiplier: I96F32 = I96F32::saturating_from_num(1.0); for _ in 0..floored_residual_int { - multiplier = multiplier.saturating_mul(I96F32::from_num(2.0)); + multiplier = multiplier.saturating_mul(I96F32::saturating_from_num(2.0)); } - let block_emission_percentage: I96F32 = I96F32::from_num(1.0).safe_div(multiplier); + let block_emission_percentage: I96F32 = + I96F32::saturating_from_num(1.0).safe_div(multiplier); // Calculate the actual emission based on the emission rate let block_emission: I96F32 = block_emission_percentage - .saturating_mul(I96F32::from_num(DefaultBlockEmission::<T>::get())); + .saturating_mul(I96F32::saturating_from_num(DefaultBlockEmission::<T>::get())); // Convert to u64 let block_emission_u64: u64 = block_emission.saturating_to_num::<u64>(); if BlockEmission::<T>::get() != block_emission_u64 { diff --git a/pallets/subtensor/src/coinbase/block_step.rs b/pallets/subtensor/src/coinbase/block_step.rs index b8b27ae73..bcfd1a37b 100644 --- a/pallets/subtensor/src/coinbase/block_step.rs +++ b/pallets/subtensor/src/coinbase/block_step.rs @@ -11,7 +11,8 @@ impl<T: Config + pallet_drand::Config> Pallet<T> { // --- 1. Adjust difficulties. Self::adjust_registration_terms_for_networks(); // --- 2. Get the current coinbase emission. - let block_emission: I96F32 = I96F32::from_num(Self::get_block_emission().unwrap_or(0)); + let block_emission: I96F32 = + I96F32::saturating_from_num(Self::get_block_emission().unwrap_or(0)); log::debug!("Block emission: {:?}", block_emission); // --- 3. Run emission through network. Self::run_coinbase(block_emission); @@ -199,25 +200,25 @@ impl<T: Config + pallet_drand::Config> Pallet<T> { registrations_this_interval: u16, target_registrations_per_interval: u16, ) -> u64 { - let updated_difficulty: I110F18 = I110F18::from_num(current_difficulty) - .saturating_mul(I110F18::from_num( + let updated_difficulty: I110F18 = I110F18::saturating_from_num(current_difficulty) + .saturating_mul(I110F18::saturating_from_num( registrations_this_interval.saturating_add(target_registrations_per_interval), )) - .safe_div(I110F18::from_num( + .safe_div(I110F18::saturating_from_num( target_registrations_per_interval.saturating_add(target_registrations_per_interval), )); - let alpha: I110F18 = I110F18::from_num(Self::get_adjustment_alpha(netuid)) - .safe_div(I110F18::from_num(u64::MAX)); + let alpha: I110F18 = I110F18::saturating_from_num(Self::get_adjustment_alpha(netuid)) + .safe_div(I110F18::saturating_from_num(u64::MAX)); let next_value: I110F18 = alpha - .saturating_mul(I110F18::from_num(current_difficulty)) + .saturating_mul(I110F18::saturating_from_num(current_difficulty)) .saturating_add( - I110F18::from_num(1.0) + I110F18::saturating_from_num(1.0) .saturating_sub(alpha) .saturating_mul(updated_difficulty), ); - if next_value >= I110F18::from_num(Self::get_max_difficulty(netuid)) { + if next_value >= I110F18::saturating_from_num(Self::get_max_difficulty(netuid)) { Self::get_max_difficulty(netuid) - } else if next_value <= I110F18::from_num(Self::get_min_difficulty(netuid)) { + } else if next_value <= I110F18::saturating_from_num(Self::get_min_difficulty(netuid)) { return Self::get_min_difficulty(netuid); } else { return next_value.saturating_to_num::<u64>(); @@ -233,25 +234,25 @@ impl<T: Config + pallet_drand::Config> Pallet<T> { registrations_this_interval: u16, target_registrations_per_interval: u16, ) -> u64 { - let updated_burn: I110F18 = I110F18::from_num(current_burn) - .saturating_mul(I110F18::from_num( + let updated_burn: I110F18 = I110F18::saturating_from_num(current_burn) + .saturating_mul(I110F18::saturating_from_num( registrations_this_interval.saturating_add(target_registrations_per_interval), )) - .safe_div(I110F18::from_num( + .safe_div(I110F18::saturating_from_num( target_registrations_per_interval.saturating_add(target_registrations_per_interval), )); - let alpha: I110F18 = I110F18::from_num(Self::get_adjustment_alpha(netuid)) - .safe_div(I110F18::from_num(u64::MAX)); + let alpha: I110F18 = I110F18::saturating_from_num(Self::get_adjustment_alpha(netuid)) + .safe_div(I110F18::saturating_from_num(u64::MAX)); let next_value: I110F18 = alpha - .saturating_mul(I110F18::from_num(current_burn)) + .saturating_mul(I110F18::saturating_from_num(current_burn)) .saturating_add( - I110F18::from_num(1.0) + I110F18::saturating_from_num(1.0) .saturating_sub(alpha) .saturating_mul(updated_burn), ); - if next_value >= I110F18::from_num(Self::get_max_burn_as_u64(netuid)) { + if next_value >= I110F18::saturating_from_num(Self::get_max_burn_as_u64(netuid)) { Self::get_max_burn_as_u64(netuid) - } else if next_value <= I110F18::from_num(Self::get_min_burn_as_u64(netuid)) { + } else if next_value <= I110F18::saturating_from_num(Self::get_min_burn_as_u64(netuid)) { return Self::get_min_burn_as_u64(netuid); } else { return next_value.saturating_to_num::<u64>(); diff --git a/pallets/subtensor/src/coinbase/root.rs b/pallets/subtensor/src/coinbase/root.rs index 9e510fa0a..3f83f934f 100644 --- a/pallets/subtensor/src/coinbase/root.rs +++ b/pallets/subtensor/src/coinbase/root.rs @@ -113,7 +113,7 @@ impl<T: Config> Pallet<T> { // --- 2. Initialize a 2D vector with zeros to store the weights. The dimensions are determined // by `n` (number of validators) and `k` (total number of subnets). - let mut weights: Vec<Vec<I64F64>> = vec![vec![I64F64::from_num(0.0); k]; n]; + let mut weights: Vec<Vec<I64F64>> = vec![vec![I64F64::saturating_from_num(0.0); k]; n]; log::debug!("weights:\n{:?}\n", weights); let subnet_list = Self::get_all_subnet_netuids(); @@ -135,7 +135,7 @@ impl<T: Config> Pallet<T> { .zip(&subnet_list) .find(|(_, subnet)| *subnet == netuid) { - *w = I64F64::from_num(*weight_ij); + *w = I64F64::saturating_from_num(*weight_ij); } } } diff --git a/pallets/subtensor/src/coinbase/run_coinbase.rs b/pallets/subtensor/src/coinbase/run_coinbase.rs index cc15d7cbe..b9d959a2e 100644 --- a/pallets/subtensor/src/coinbase/run_coinbase.rs +++ b/pallets/subtensor/src/coinbase/run_coinbase.rs @@ -25,15 +25,16 @@ impl<T: Config> Pallet<T> { validator_proportion: I96F32, ) -> I96F32 { // Get total TAO on root. - let total_root_tao: I96F32 = I96F32::from_num(SubnetTAO::<T>::get(0)); + let total_root_tao: I96F32 = I96F32::saturating_from_num(SubnetTAO::<T>::get(0)); // Get total ALPHA on subnet. - let total_alpha_issuance: I96F32 = I96F32::from_num(Self::get_alpha_issuance(netuid)); + let total_alpha_issuance: I96F32 = + I96F32::saturating_from_num(Self::get_alpha_issuance(netuid)); // Get tao_weight let tao_weight: I96F32 = total_root_tao.saturating_mul(Self::get_tao_weight()); // Get root proportional dividends. let root_proportion: I96F32 = tao_weight .checked_div(tao_weight.saturating_add(total_alpha_issuance)) - .unwrap_or(I96F32::from_num(0.0)); + .unwrap_or(I96F32::saturating_from_num(0.0)); // Get root proportion of alpha_out dividends. let root_divs_in_alpha: I96F32 = root_proportion .saturating_mul(alpha_out_emission) @@ -54,17 +55,22 @@ impl<T: Config> Pallet<T> { // --- 2. Sum all the SubnetTAO associated with the same mechanism. // Mechanisms get emission based on the proportion of TAO across all their subnets - let mut total_active_tao: I96F32 = I96F32::from_num(0); + let mut total_active_tao: I96F32 = I96F32::saturating_from_num(0); let mut mechanism_tao: BTreeMap<u16, I96F32> = BTreeMap::new(); for netuid in subnets.iter() { if *netuid == 0 { continue; } // Skip root network let mechid = SubnetMechanism::<T>::get(*netuid); - let subnet_tao = I96F32::from_num(SubnetTAO::<T>::get(*netuid)); - let new_subnet_tao = subnet_tao - .saturating_add(*mechanism_tao.entry(mechid).or_insert(I96F32::from_num(0))); - *mechanism_tao.entry(mechid).or_insert(I96F32::from_num(0)) = new_subnet_tao; + let subnet_tao = I96F32::saturating_from_num(SubnetTAO::<T>::get(*netuid)); + let new_subnet_tao = subnet_tao.saturating_add( + *mechanism_tao + .entry(mechid) + .or_insert(I96F32::saturating_from_num(0)), + ); + *mechanism_tao + .entry(mechid) + .or_insert(I96F32::saturating_from_num(0)) = new_subnet_tao; total_active_tao = total_active_tao.saturating_add(subnet_tao); } log::debug!("Mechanism TAO sums: {:?}", mechanism_tao); @@ -80,10 +86,12 @@ impl<T: Config> Pallet<T> { let mechid: u16 = SubnetMechanism::<T>::get(*netuid); log::debug!("Netuid: {:?}, Mechanism ID: {:?}", netuid, mechid); // 3.2: Get subnet TAO (T_s) - let subnet_tao: I96F32 = I96F32::from_num(SubnetTAO::<T>::get(*netuid)); + let subnet_tao: I96F32 = I96F32::saturating_from_num(SubnetTAO::<T>::get(*netuid)); log::debug!("Subnet TAO (T_s) for netuid {:?}: {:?}", netuid, subnet_tao); // 3.3: Get the denominator as the sum of all TAO associated with a specific mechanism (T_m) - let mech_tao: I96F32 = *mechanism_tao.get(&mechid).unwrap_or(&I96F32::from_num(0)); + let mech_tao: I96F32 = *mechanism_tao + .get(&mechid) + .unwrap_or(&I96F32::saturating_from_num(0)); log::debug!( "Mechanism TAO (T_m) for mechanism ID {:?}: {:?}", mechid, @@ -92,7 +100,7 @@ impl<T: Config> Pallet<T> { // 3.4: Compute the mechanism emission proportion: P_m = T_m / T_total let mech_proportion: I96F32 = mech_tao .checked_div(total_active_tao) - .unwrap_or(I96F32::from_num(0)); + .unwrap_or(I96F32::saturating_from_num(0)); log::debug!( "Mechanism proportion (P_m) for mechanism ID {:?}: {:?}", mechid, @@ -108,7 +116,7 @@ impl<T: Config> Pallet<T> { // 3.6: Calculate subnet's proportion of mechanism TAO: P_s = T_s / T_m let subnet_proportion: I96F32 = subnet_tao .checked_div(mech_tao) - .unwrap_or(I96F32::from_num(0)); + .unwrap_or(I96F32::saturating_from_num(0)); log::debug!( "Subnet proportion (P_s) for netuid {:?}: {:?}", netuid, @@ -117,7 +125,7 @@ impl<T: Config> Pallet<T> { // 3.7: Calculate subnet's TAO emission: E_s = P_s * E_m let tao_in: u64 = mech_emission .checked_mul(subnet_proportion) - .unwrap_or(I96F32::from_num(0)) + .unwrap_or(I96F32::saturating_from_num(0)) .saturating_to_num::<u64>(); log::debug!( "Subnet TAO emission (E_s) for netuid {:?}: {:?}", @@ -199,7 +207,7 @@ impl<T: Config> Pallet<T> { }); // Calculate the owner cut. - let owner_cut: u64 = I96F32::from_num(alpha_out_emission) + let owner_cut: u64 = I96F32::saturating_from_num(alpha_out_emission) .saturating_mul(Self::get_float_subnet_owner_cut()) .saturating_to_num::<u64>(); log::debug!("Owner cut for netuid {:?}: {:?}", netuid, owner_cut); @@ -214,16 +222,16 @@ impl<T: Config> Pallet<T> { ); // Validators get 50% of remaining emission. - let validator_proportion: I96F32 = I96F32::from_num(0.5); + let validator_proportion: I96F32 = I96F32::saturating_from_num(0.5); // Get proportion of alpha out emission as root divs. let root_emission_in_alpha: I96F32 = Self::get_root_divs_in_alpha( *netuid, - I96F32::from_num(remaining_emission), + I96F32::saturating_from_num(remaining_emission), validator_proportion, ); // Subtract root divs from alpha divs. - let pending_alpha_emission: I96F32 = - I96F32::from_num(remaining_emission).saturating_sub(root_emission_in_alpha); + let pending_alpha_emission: I96F32 = I96F32::saturating_from_num(remaining_emission) + .saturating_sub(root_emission_in_alpha); // Sell root emission through the pool. let root_emission_in_tao: u64 = Self::swap_alpha_for_tao( *netuid, @@ -363,22 +371,21 @@ impl<T: Config> Pallet<T> { // Calculate the validator take and root alpha divs using the alpha divs. for (hotkey, dividend_tuples) in dividends_to_distribute.iter() { // Get the local alpha and root alpha. - let hotkey_tao: I96F32 = I96F32::from_num(Self::get_stake_for_hotkey_on_subnet( - hotkey, - Self::get_root_netuid(), - )); + let hotkey_tao: I96F32 = I96F32::saturating_from_num( + Self::get_stake_for_hotkey_on_subnet(hotkey, Self::get_root_netuid()), + ); let hotkey_tao_as_alpha: I96F32 = hotkey_tao.saturating_mul(Self::get_tao_weight()); let hotkey_alpha = - I96F32::from_num(Self::get_stake_for_hotkey_on_subnet(hotkey, netuid)); + I96F32::saturating_from_num(Self::get_stake_for_hotkey_on_subnet(hotkey, netuid)); log::debug!("Hotkey tao for hotkey {:?} on root netuid: {:?}, hotkey tao as alpha: {:?}, hotkey alpha: {:?}", hotkey, hotkey_tao, hotkey_tao_as_alpha, hotkey_alpha); // Compute alpha and root proportions. let alpha_prop: I96F32 = hotkey_alpha .checked_div(hotkey_alpha.saturating_add(hotkey_tao_as_alpha)) - .unwrap_or(I96F32::from_num(0.0)); + .unwrap_or(I96F32::saturating_from_num(0.0)); let root_prop: I96F32 = hotkey_tao_as_alpha .checked_div(hotkey_alpha.saturating_add(hotkey_tao_as_alpha)) - .unwrap_or(I96F32::from_num(0.0)); + .unwrap_or(I96F32::saturating_from_num(0.0)); log::debug!( "Alpha proportion: {:?}, root proportion: {:?}", alpha_prop, @@ -395,11 +402,14 @@ impl<T: Config> Pallet<T> { ); // Remove the hotkey take straight off the top. - let take_prop: I96F32 = I96F32::from_num(Self::get_hotkey_take(hotkey_j)) - .checked_div(I96F32::from_num(u16::MAX)) - .unwrap_or(I96F32::from_num(0.0)); - let validator_take: I96F32 = take_prop.saturating_mul(I96F32::from_num(*divs_j)); - let rem_divs_j: I96F32 = I96F32::from_num(*divs_j).saturating_sub(validator_take); + let take_prop: I96F32 = + I96F32::saturating_from_num(Self::get_hotkey_take(hotkey_j)) + .checked_div(I96F32::saturating_from_num(u16::MAX)) + .unwrap_or(I96F32::saturating_from_num(0.0)); + let validator_take: I96F32 = + take_prop.saturating_mul(I96F32::saturating_from_num(*divs_j)); + let rem_divs_j: I96F32 = + I96F32::saturating_from_num(*divs_j).saturating_sub(validator_take); log::debug!( "Validator take for hotkey {:?}: {:?}, remaining dividends: {:?}", hotkey_j, @@ -466,11 +476,14 @@ impl<T: Config> Pallet<T> { // Pay out dividends to hotkeys based on the local vs root proportion. for (hotkey_j, divs_j) in dividend_tuples.iter() { // Remove the hotkey take straight off the top. - let take_prop: I96F32 = I96F32::from_num(Self::get_hotkey_take(hotkey_j)) - .checked_div(I96F32::from_num(u16::MAX)) - .unwrap_or(I96F32::from_num(0.0)); - let validator_take: I96F32 = take_prop.saturating_mul(I96F32::from_num(*divs_j)); - let rem_divs_j: I96F32 = I96F32::from_num(*divs_j).saturating_sub(validator_take); + let take_prop: I96F32 = + I96F32::saturating_from_num(Self::get_hotkey_take(hotkey_j)) + .checked_div(I96F32::saturating_from_num(u16::MAX)) + .unwrap_or(I96F32::saturating_from_num(0.0)); + let validator_take: I96F32 = + take_prop.saturating_mul(I96F32::saturating_from_num(*divs_j)); + let rem_divs_j: I96F32 = + I96F32::saturating_from_num(*divs_j).saturating_sub(validator_take); log::debug!( "Validator take for hotkey {:?}: {:?}, remaining dividends: {:?}", hotkey_j, @@ -522,11 +535,11 @@ impl<T: Config> Pallet<T> { let _ = TaoDividendsPerSubnet::<T>::clear_prefix(netuid, u32::MAX, None); for (hotkey_j, root_divs) in root_alpha_divs.iter() { - let proportion: I96F32 = I96F32::from_num(*root_divs) - .checked_div(I96F32::from_num(total_root_alpha_divs)) - .unwrap_or(I96F32::from_num(0)); + let proportion: I96F32 = I96F32::saturating_from_num(*root_divs) + .checked_div(I96F32::saturating_from_num(total_root_alpha_divs)) + .unwrap_or(I96F32::saturating_from_num(0)); let root_divs_to_pay: u64 = proportion - .saturating_mul(I96F32::from_num(pending_root_divs)) + .saturating_mul(I96F32::saturating_from_num(pending_root_divs)) .saturating_to_num::<u64>(); log::debug!( "Proportion for hotkey {:?}: {:?}, root_divs_to_pay: {:?}", @@ -559,11 +572,11 @@ impl<T: Config> Pallet<T> { pub fn get_self_contribution(hotkey: &T::AccountId, netuid: u16) -> u64 { // Get all childkeys for this hotkey. let childkeys = Self::get_children(hotkey, netuid); - let mut remaining_proportion: I96F32 = I96F32::from_num(1.0); + let mut remaining_proportion: I96F32 = I96F32::saturating_from_num(1.0); for (proportion, _) in childkeys { remaining_proportion = remaining_proportion.saturating_sub( - I96F32::from_num(proportion) // Normalize - .safe_div(I96F32::from_num(u64::MAX)), + I96F32::saturating_from_num(proportion) // Normalize + .safe_div(I96F32::saturating_from_num(u64::MAX)), ); } @@ -571,12 +584,12 @@ impl<T: Config> Pallet<T> { let tao_weight: I96F32 = Self::get_tao_weight(); // Get the hotkey's stake including weight - let root_stake: I96F32 = I96F32::from_num(Self::get_stake_for_hotkey_on_subnet( + let root_stake: I96F32 = I96F32::saturating_from_num(Self::get_stake_for_hotkey_on_subnet( hotkey, Self::get_root_netuid(), )); let alpha_stake: I96F32 = - I96F32::from_num(Self::get_stake_for_hotkey_on_subnet(hotkey, netuid)); + I96F32::saturating_from_num(Self::get_stake_for_hotkey_on_subnet(hotkey, netuid)); // Calculate the let alpha_contribution: I96F32 = alpha_stake.saturating_mul(remaining_proportion); @@ -611,10 +624,10 @@ impl<T: Config> Pallet<T> { let mut dividend_tuples: Vec<(T::AccountId, u64)> = vec![]; // Calculate the hotkey's share of the validator emission based on its childkey take - let validating_emission: I96F32 = I96F32::from_num(dividends); + let validating_emission: I96F32 = I96F32::saturating_from_num(dividends); let childkey_take_proportion: I96F32 = - I96F32::from_num(Self::get_childkey_take(hotkey, netuid)) - .safe_div(I96F32::from_num(u16::MAX)); + I96F32::saturating_from_num(Self::get_childkey_take(hotkey, netuid)) + .safe_div(I96F32::saturating_from_num(u16::MAX)); log::debug!( "Childkey take proportion: {:?} for hotkey {:?}", childkey_take_proportion, @@ -623,8 +636,8 @@ impl<T: Config> Pallet<T> { // NOTE: Only the validation emission should be split amongst parents. // Reserve childkey take - let child_emission_take: I96F32 = - childkey_take_proportion.saturating_mul(I96F32::from_num(validating_emission)); + let child_emission_take: I96F32 = childkey_take_proportion + .saturating_mul(I96F32::saturating_from_num(validating_emission)); let remaining_emission: I96F32 = validating_emission.saturating_sub(child_emission_take); log::debug!( "Child emission take: {:?} for hotkey {:?}", @@ -641,7 +654,7 @@ impl<T: Config> Pallet<T> { let mut to_parents: u64 = 0; // Initialize variables to calculate total stakes from parents - let mut total_contribution: I96F32 = I96F32::from_num(0); + let mut total_contribution: I96F32 = I96F32::saturating_from_num(0); let mut parent_contributions: Vec<(T::AccountId, I96F32)> = Vec::new(); // Get the weights for root and alpha stakes in emission distribution @@ -656,21 +669,21 @@ impl<T: Config> Pallet<T> { self_contribution ); // Add self contribution to total contribution but not to the parent contributions. - total_contribution = total_contribution.saturating_add(I96F32::from_num(self_contribution)); + total_contribution = + total_contribution.saturating_add(I96F32::saturating_from_num(self_contribution)); // Calculate total root and alpha (subnet-specific) stakes from all parents for (proportion, parent) in Self::get_parents(hotkey, netuid) { // Convert the parent's stake proportion to a fractional value - let parent_proportion: I96F32 = - I96F32::from_num(proportion).safe_div(I96F32::from_num(u64::MAX)); + let parent_proportion: I96F32 = I96F32::saturating_from_num(proportion) + .safe_div(I96F32::saturating_from_num(u64::MAX)); // Get the parent's root and subnet-specific (alpha) stakes - let parent_root: I96F32 = I96F32::from_num(Self::get_stake_for_hotkey_on_subnet( - &parent, - Self::get_root_netuid(), - )); + let parent_root: I96F32 = I96F32::saturating_from_num( + Self::get_stake_for_hotkey_on_subnet(&parent, Self::get_root_netuid()), + ); let parent_alpha: I96F32 = - I96F32::from_num(Self::get_stake_for_hotkey_on_subnet(&parent, netuid)); + I96F32::saturating_from_num(Self::get_stake_for_hotkey_on_subnet(&parent, netuid)); // Calculate the parent's contribution to the hotkey's stakes let parent_alpha_contribution: I96F32 = parent_alpha.saturating_mul(parent_proportion); @@ -698,7 +711,7 @@ impl<T: Config> Pallet<T> { // Sum up the total emission for this parent let emission_factor: I96F32 = contribution .checked_div(total_contribution) - .unwrap_or(I96F32::from_num(0)); + .unwrap_or(I96F32::saturating_from_num(0)); let parent_emission: u64 = (remaining_emission.saturating_mul(emission_factor)).saturating_to_num::<u64>(); diff --git a/pallets/subtensor/src/epoch/math.rs b/pallets/subtensor/src/epoch/math.rs index e57d63b3a..9818b06a4 100644 --- a/pallets/subtensor/src/epoch/math.rs +++ b/pallets/subtensor/src/epoch/math.rs @@ -17,7 +17,7 @@ use sp_std::vec::Vec; #[allow(dead_code)] pub fn fixed(val: f32) -> I32F32 { - I32F32::from_num(val) + I32F32::saturating_from_num(val) } #[allow(dead_code)] @@ -37,27 +37,27 @@ pub fn fixed64_to_u64(x: I64F64) -> u64 { #[allow(dead_code)] pub fn fixed64_to_fixed32(x: I64F64) -> I32F32 { - I32F32::from_num(x) + I32F32::saturating_from_num(x) } #[allow(dead_code)] pub fn fixed32_to_fixed64(x: I32F32) -> I64F64 { - I64F64::from_num(x) + I64F64::saturating_from_num(x) } #[allow(dead_code)] pub fn u16_to_fixed(x: u16) -> I32F32 { - I32F32::from_num(x) + I32F32::saturating_from_num(x) } #[allow(dead_code)] pub fn u16_proportion_to_fixed(x: u16) -> I32F32 { - I32F32::from_num(x).safe_div(I32F32::from_num(u16::MAX)) + I32F32::saturating_from_num(x).safe_div(I32F32::saturating_from_num(u16::MAX)) } #[allow(dead_code)] pub fn fixed_proportion_to_u16(x: I32F32) -> u16 { - fixed_to_u16(x.saturating_mul(I32F32::from_num(u16::MAX))) + fixed_to_u16(x.saturating_mul(I32F32::saturating_from_num(u16::MAX))) } #[allow(dead_code)] @@ -93,12 +93,12 @@ pub fn vec_fixed_proportions_to_u16(vec: Vec<I32F32>) -> Vec<u16> { #[allow(dead_code)] // Max-upscale vector and convert to u16 so max_value = u16::MAX. Assumes non-negative normalized input. pub fn vec_max_upscale_to_u16(vec: &[I32F32]) -> Vec<u16> { - let u16_max: I32F32 = I32F32::from_num(u16::MAX); - let threshold: I32F32 = I32F32::from_num(32768); + let u16_max: I32F32 = I32F32::saturating_from_num(u16::MAX); + let threshold: I32F32 = I32F32::saturating_from_num(32768); let max_value: Option<&I32F32> = vec.iter().max(); match max_value { Some(val) => { - if *val == I32F32::from_num(0) { + if *val == I32F32::saturating_from_num(0) { return vec .iter() .map(|e: &I32F32| e.saturating_mul(u16_max).saturating_to_num::<u16>()) @@ -139,15 +139,22 @@ pub fn vec_max_upscale_to_u16(vec: &[I32F32]) -> Vec<u16> { #[allow(dead_code)] // Max-upscale u16 vector and convert to u16 so max_value = u16::MAX. Assumes u16 vector input. pub fn vec_u16_max_upscale_to_u16(vec: &[u16]) -> Vec<u16> { - let vec_fixed: Vec<I32F32> = vec.iter().map(|e: &u16| I32F32::from_num(*e)).collect(); + let vec_fixed: Vec<I32F32> = vec + .iter() + .map(|e: &u16| I32F32::saturating_from_num(*e)) + .collect(); vec_max_upscale_to_u16(&vec_fixed) } #[allow(dead_code)] // Checks if u16 vector, when normalized, has a max value not greater than a u16 ratio max_limit. pub fn check_vec_max_limited(vec: &[u16], max_limit: u16) -> bool { - let max_limit_fixed: I32F32 = I32F32::from_num(max_limit).safe_div(I32F32::from_num(u16::MAX)); - let mut vec_fixed: Vec<I32F32> = vec.iter().map(|e: &u16| I32F32::from_num(*e)).collect(); + let max_limit_fixed: I32F32 = + I32F32::saturating_from_num(max_limit).safe_div(I32F32::saturating_from_num(u16::MAX)); + let mut vec_fixed: Vec<I32F32> = vec + .iter() + .map(|e: &u16| I32F32::saturating_from_num(*e)) + .collect(); inplace_normalize(&mut vec_fixed); let max_value: Option<&I32F32> = vec_fixed.iter().max(); max_value.is_none_or(|v| *v <= max_limit_fixed) @@ -180,14 +187,14 @@ where #[allow(dead_code)] pub fn is_zero(vector: &[I32F32]) -> bool { let vector_sum: I32F32 = sum(vector); - vector_sum == I32F32::from_num(0) + vector_sum == I32F32::saturating_from_num(0) } // Exp safe function with I32F32 output of I32F32 input. #[allow(dead_code)] pub fn exp_safe(input: I32F32) -> I32F32 { - let min_input: I32F32 = I32F32::from_num(-20); // <= 1/exp(-20) = 485 165 195,4097903 - let max_input: I32F32 = I32F32::from_num(20); // <= exp(20) = 485 165 195,4097903 + let min_input: I32F32 = I32F32::saturating_from_num(-20); // <= 1/exp(-20) = 485 165 195,4097903 + let max_input: I32F32 = I32F32::saturating_from_num(20); // <= exp(20) = 485 165 195,4097903 let mut safe_input: I32F32 = input; if input < min_input { safe_input = min_input; @@ -201,7 +208,7 @@ pub fn exp_safe(input: I32F32) -> I32F32 { } Err(_err) => { if safe_input <= 0 { - output = I32F32::from_num(0); + output = I32F32::saturating_from_num(0); } else { output = I32F32::max_value(); } @@ -213,7 +220,7 @@ pub fn exp_safe(input: I32F32) -> I32F32 { // Sigmoid safe function with I32F32 output of I32F32 input with offset kappa and (recommended) scaling 0 < rho <= 40. #[allow(dead_code)] pub fn sigmoid_safe(input: I32F32, rho: I32F32, kappa: I32F32) -> I32F32 { - let one: I32F32 = I32F32::from_num(1); + let one: I32F32 = I32F32::saturating_from_num(1); let offset: I32F32 = input.saturating_sub(kappa); // (input - kappa) let neg_rho: I32F32 = rho.saturating_mul(one.saturating_neg()); // -rho let exp_input: I32F32 = neg_rho.saturating_mul(offset); // -rho*(input-kappa) @@ -243,7 +250,7 @@ pub fn is_topk(vector: &[I32F32], k: usize) -> Vec<bool> { #[allow(dead_code)] pub fn normalize(x: &[I32F32]) -> Vec<I32F32> { let x_sum: I32F32 = sum(x); - if x_sum != I32F32::from_num(0.0_f32) { + if x_sum != I32F32::saturating_from_num(0.0_f32) { x.iter().map(|xi| xi.safe_div(x_sum)).collect() } else { x.to_vec() @@ -254,7 +261,7 @@ pub fn normalize(x: &[I32F32]) -> Vec<I32F32> { #[allow(dead_code)] pub fn inplace_normalize(x: &mut [I32F32]) { let x_sum: I32F32 = x.iter().sum(); - if x_sum == I32F32::from_num(0.0_f32) { + if x_sum == I32F32::saturating_from_num(0.0_f32) { return; } x.iter_mut() @@ -264,7 +271,7 @@ pub fn inplace_normalize(x: &mut [I32F32]) { // Normalizes (sum to 1 except 0) the input vector directly in-place, using the sum arg. #[allow(dead_code)] pub fn inplace_normalize_using_sum(x: &mut [I32F32], x_sum: I32F32) { - if x_sum == I32F32::from_num(0.0_f32) { + if x_sum == I32F32::saturating_from_num(0.0_f32) { return; } x.iter_mut() @@ -275,7 +282,7 @@ pub fn inplace_normalize_using_sum(x: &mut [I32F32], x_sum: I32F32) { #[allow(dead_code)] pub fn inplace_normalize_64(x: &mut [I64F64]) { let x_sum: I64F64 = x.iter().sum(); - if x_sum == I64F64::from_num(0) { + if x_sum == I64F64::saturating_from_num(0) { return; } x.iter_mut() @@ -287,7 +294,7 @@ pub fn inplace_normalize_64(x: &mut [I64F64]) { pub fn inplace_row_normalize_64(x: &mut [Vec<I64F64>]) { for row in x { let row_sum: I64F64 = row.iter().sum(); - if row_sum > I64F64::from_num(0.0_f64) { + if row_sum > I64F64::saturating_from_num(0.0_f64) { row.iter_mut() .for_each(|x_ij: &mut I64F64| *x_ij = x_ij.safe_div(row_sum)); } @@ -304,7 +311,7 @@ pub fn vecdiv(x: &[I32F32], y: &[I32F32]) -> Vec<I32F32> { if *y_i != 0 { x_i.safe_div(*y_i) } else { - I32F32::from_num(0) + I32F32::saturating_from_num(0) } }) .collect() @@ -315,7 +322,7 @@ pub fn vecdiv(x: &[I32F32], y: &[I32F32]) -> Vec<I32F32> { pub fn inplace_row_normalize(x: &mut [Vec<I32F32>]) { for row in x { let row_sum: I32F32 = row.iter().sum(); - if row_sum > I32F32::from_num(0.0_f32) { + if row_sum > I32F32::saturating_from_num(0.0_f32) { row.iter_mut() .for_each(|x_ij: &mut I32F32| *x_ij = x_ij.safe_div(row_sum)); } @@ -327,7 +334,7 @@ pub fn inplace_row_normalize(x: &mut [Vec<I32F32>]) { pub fn inplace_row_normalize_sparse(sparse_matrix: &mut [Vec<(u16, I32F32)>]) { for sparse_row in sparse_matrix.iter_mut() { let row_sum: I32F32 = sparse_row.iter().map(|(_j, value)| *value).sum(); - if row_sum > I32F32::from_num(0.0) { + if row_sum > I32F32::saturating_from_num(0.0) { sparse_row .iter_mut() .for_each(|(_j, value)| *value = value.safe_div(row_sum)); @@ -365,19 +372,21 @@ pub fn col_sum(x: &[Vec<I32F32>]) -> Vec<I32F32> { if cols == 0 { return vec![]; } - x.iter() - .fold(vec![I32F32::from_num(0); cols], |acc, next_row| { + x.iter().fold( + vec![I32F32::saturating_from_num(0); cols], + |acc, next_row| { acc.into_iter() .zip(next_row) .map(|(acc_elem, next_elem)| acc_elem.saturating_add(*next_elem)) .collect() - }) + }, + ) } // Sum across each column (dim=1) of a sparse matrix. #[allow(dead_code, clippy::indexing_slicing)] pub fn col_sum_sparse(sparse_matrix: &[Vec<(u16, I32F32)>], columns: u16) -> Vec<I32F32> { - let mut result: Vec<I32F32> = vec![I32F32::from_num(0); columns as usize]; + let mut result: Vec<I32F32> = vec![I32F32::saturating_from_num(0); columns as usize]; for sparse_row in sparse_matrix { for (j, value) in sparse_row { result[*j as usize] = result[*j as usize].saturating_add(*value); @@ -389,7 +398,7 @@ pub fn col_sum_sparse(sparse_matrix: &[Vec<(u16, I32F32)>], columns: u16) -> Vec // Normalizes (sum to 1 except 0) each column (dim=1) of a sparse matrix in-place. #[allow(dead_code, clippy::indexing_slicing)] pub fn inplace_col_normalize_sparse(sparse_matrix: &mut [Vec<(u16, I32F32)>], columns: u16) { - let mut col_sum: Vec<I32F32> = vec![I32F32::from_num(0.0); columns as usize]; // assume square matrix, rows=cols + let mut col_sum: Vec<I32F32> = vec![I32F32::saturating_from_num(0.0); columns as usize]; // assume square matrix, rows=cols for sparse_row in sparse_matrix.iter() { for (j, value) in sparse_row.iter() { col_sum[*j as usize] = col_sum[*j as usize].saturating_add(*value); @@ -397,7 +406,7 @@ pub fn inplace_col_normalize_sparse(sparse_matrix: &mut [Vec<(u16, I32F32)>], co } for sparse_row in sparse_matrix { for (j, value) in sparse_row { - if col_sum[*j as usize] == I32F32::from_num(0.0_f32) { + if col_sum[*j as usize] == I32F32::saturating_from_num(0.0_f32) { continue; } *value = value.safe_div(col_sum[*j as usize]); @@ -417,7 +426,7 @@ pub fn inplace_col_normalize(x: &mut [Vec<I32F32>]) { let cols = first_row.len(); let col_sums = x .iter_mut() - .fold(vec![I32F32::from_num(0.0); cols], |acc, row| { + .fold(vec![I32F32::saturating_from_num(0.0); cols], |acc, row| { row.iter_mut() .zip(acc) .map(|(&mut m_val, acc_val)| acc_val.saturating_add(m_val)) @@ -426,7 +435,7 @@ pub fn inplace_col_normalize(x: &mut [Vec<I32F32>]) { x.iter_mut().for_each(|row| { row.iter_mut() .zip(&col_sums) - .filter(|(_, col_sum)| **col_sum != I32F32::from_num(0_f32)) + .filter(|(_, col_sum)| **col_sum != I32F32::saturating_from_num(0_f32)) .for_each(|(m_val, col_sum)| { *m_val = m_val.safe_div(*col_sum); }); @@ -436,7 +445,7 @@ pub fn inplace_col_normalize(x: &mut [Vec<I32F32>]) { // Max-upscale each column (dim=1) of a sparse matrix in-place. #[allow(dead_code, clippy::indexing_slicing)] pub fn inplace_col_max_upscale_sparse(sparse_matrix: &mut [Vec<(u16, I32F32)>], columns: u16) { - let mut col_max: Vec<I32F32> = vec![I32F32::from_num(0.0); columns as usize]; // assume square matrix, rows=cols + let mut col_max: Vec<I32F32> = vec![I32F32::saturating_from_num(0.0); columns as usize]; // assume square matrix, rows=cols for sparse_row in sparse_matrix.iter() { for (j, value) in sparse_row.iter() { if col_max[*j as usize] < *value { @@ -446,7 +455,7 @@ pub fn inplace_col_max_upscale_sparse(sparse_matrix: &mut [Vec<(u16, I32F32)>], } for sparse_row in sparse_matrix { for (j, value) in sparse_row { - if col_max[*j as usize] == I32F32::from_num(0.0_f32) { + if col_max[*j as usize] == I32F32::saturating_from_num(0.0_f32) { continue; } *value = value.safe_div(col_max[*j as usize]); @@ -464,18 +473,19 @@ pub fn inplace_col_max_upscale(x: &mut [Vec<I32F32>]) { return; } let cols = first_row.len(); - let col_maxes = x - .iter_mut() - .fold(vec![I32F32::from_num(0_f32); cols], |acc, row| { + let col_maxes = x.iter_mut().fold( + vec![I32F32::saturating_from_num(0_f32); cols], + |acc, row| { row.iter_mut() .zip(acc) .map(|(m_val, acc_val)| acc_val.max(*m_val)) .collect() - }); + }, + ); x.iter_mut().for_each(|row| { row.iter_mut() .zip(&col_maxes) - .filter(|(_, col_max)| **col_max != I32F32::from_num(0)) + .filter(|(_, col_max)| **col_max != I32F32::saturating_from_num(0)) .for_each(|(m_val, col_max)| { *m_val = m_val.safe_div(*col_max); }); @@ -489,7 +499,7 @@ pub fn inplace_mask_vector(mask: &[bool], vector: &mut [I32F32]) { return; } assert_eq!(mask.len(), vector.len()); - let zero: I32F32 = I32F32::from_num(0.0); + let zero: I32F32 = I32F32::saturating_from_num(0.0); mask.iter() .zip(vector) .filter(|(m, _)| **m) @@ -508,7 +518,7 @@ pub fn inplace_mask_matrix(mask: &[Vec<bool>], matrix: &mut Vec<Vec<I32F32>>) { return; } assert_eq!(mask.len(), matrix.len()); - let zero: I32F32 = I32F32::from_num(0.0); + let zero: I32F32 = I32F32::saturating_from_num(0.0); mask.iter().zip(matrix).for_each(|(mask_row, matrix_row)| { mask_row .iter() @@ -528,7 +538,7 @@ pub fn inplace_mask_rows(mask: &[bool], matrix: &mut [Vec<I32F32>]) { }; let cols = first_row.len(); assert_eq!(mask.len(), matrix.len()); - let zero: I32F32 = I32F32::from_num(0); + let zero: I32F32 = I32F32::saturating_from_num(0); matrix .iter_mut() .zip(mask) @@ -549,7 +559,7 @@ pub fn inplace_mask_diag(matrix: &mut [Vec<I32F32>]) { return; } assert_eq!(matrix.len(), first_row.len()); - let zero: I32F32 = I32F32::from_num(0.0); + let zero: I32F32 = I32F32::saturating_from_num(0.0); matrix.iter_mut().enumerate().for_each(|(idx, row)| { let Some(elem) = row.get_mut(idx) else { // Should not happen since matrix is square @@ -664,7 +674,7 @@ pub fn matmul(matrix: &[Vec<I32F32>], vector: &[I32F32]) -> Vec<I32F32> { } assert!(matrix.len() == vector.len()); matrix.iter().zip(vector).fold( - vec![I32F32::from_num(0_f32); cols], + vec![I32F32::saturating_from_num(0_f32); cols], |acc, (row, vec_val)| { row.iter() .zip(acc) @@ -690,10 +700,9 @@ pub fn matmul_64(matrix: &[Vec<I64F64>], vector: &[I64F64]) -> Vec<I64F64> { return vec![]; } assert!(matrix.len() == vector.len()); - matrix - .iter() - .zip(vector) - .fold(vec![I64F64::from_num(0.0); cols], |acc, (row, vec_val)| { + matrix.iter().zip(vector).fold( + vec![I64F64::saturating_from_num(0.0); cols], + |acc, (row, vec_val)| { row.iter() .zip(acc) .map(|(m_val, acc_val)| { @@ -703,7 +712,8 @@ pub fn matmul_64(matrix: &[Vec<I64F64>], vector: &[I64F64]) -> Vec<I64F64> { acc_val.saturating_add(vec_val.saturating_mul(*m_val)) }) .collect() - }) + }, + ) } // Column-wise matrix-vector product, row-wise sum: result_i = SUM(j) vector_j * matrix_ij. @@ -721,7 +731,7 @@ pub fn matmul_transpose(matrix: &[Vec<I32F32>], vector: &[I32F32]) -> Vec<I32F32 .map(|row| { row.iter() .zip(vector) - .fold(I32F32::from_num(0.0), |acc, (velem, melem)| { + .fold(I32F32::saturating_from_num(0.0), |acc, (velem, melem)| { // Compute dividends: d_j = SUM(i) b_ji * inc_i // result_j = SUM(i) vector_i * matrix_ji // result_i = SUM(j) vector_j * matrix_ij @@ -738,7 +748,7 @@ pub fn matmul_sparse( vector: &[I32F32], columns: u16, ) -> Vec<I32F32> { - let mut result: Vec<I32F32> = vec![I32F32::from_num(0.0); columns as usize]; + let mut result: Vec<I32F32> = vec![I32F32::saturating_from_num(0.0); columns as usize]; for (i, sparse_row) in sparse_matrix.iter().enumerate() { for (j, value) in sparse_row.iter() { // Compute ranks: r_j = SUM(i) w_ij * s_i @@ -757,7 +767,7 @@ pub fn matmul_transpose_sparse( sparse_matrix: &[Vec<(u16, I32F32)>], vector: &[I32F32], ) -> Vec<I32F32> { - let mut result: Vec<I32F32> = vec![I32F32::from_num(0.0); sparse_matrix.len()]; + let mut result: Vec<I32F32> = vec![I32F32::saturating_from_num(0.0); sparse_matrix.len()]; for (i, sparse_row) in sparse_matrix.iter().enumerate() { for (j, value) in sparse_row.iter() { // Compute dividends: d_j = SUM(i) b_ji * inc_i @@ -892,7 +902,7 @@ pub fn weighted_median( ) -> I32F32 { let n = partition_idx.len(); if n == 0 { - return I32F32::from_num(0); + return I32F32::saturating_from_num(0); } if n == 1 { return score[partition_idx[0]]; @@ -900,8 +910,8 @@ pub fn weighted_median( assert!(stake.len() == score.len()); let mid_idx: usize = n.safe_div(2); let pivot: I32F32 = score[partition_idx[mid_idx]]; - let mut lo_stake: I32F32 = I32F32::from_num(0); - let mut hi_stake: I32F32 = I32F32::from_num(0); + let mut lo_stake: I32F32 = I32F32::saturating_from_num(0); + let mut hi_stake: I32F32 = I32F32::saturating_from_num(0); let mut lower: Vec<usize> = vec![]; let mut upper: Vec<usize> = vec![]; for &idx in partition_idx { @@ -951,7 +961,7 @@ pub fn weighted_median_col( ) -> Vec<I32F32> { let rows = stake.len(); let columns = score[0].len(); - let zero: I32F32 = I32F32::from_num(0); + let zero: I32F32 = I32F32::saturating_from_num(0); let mut median: Vec<I32F32> = vec![zero; columns]; #[allow(clippy::needless_range_loop)] @@ -991,7 +1001,7 @@ pub fn weighted_median_col_sparse( majority: I32F32, ) -> Vec<I32F32> { let rows = stake.len(); - let zero: I32F32 = I32F32::from_num(0); + let zero: I32F32 = I32F32::saturating_from_num(0); let mut use_stake: Vec<I32F32> = stake.iter().copied().filter(|&s| s > zero).collect(); inplace_normalize(&mut use_stake); let stake_sum: I32F32 = use_stake.iter().sum(); @@ -1028,10 +1038,10 @@ pub fn weighted_median_col_sparse( // ratio=1: Result = B #[allow(dead_code)] pub fn interpolate(mat1: &[Vec<I32F32>], mat2: &[Vec<I32F32>], ratio: I32F32) -> Vec<Vec<I32F32>> { - if ratio == I32F32::from_num(0) { + if ratio == I32F32::saturating_from_num(0) { return mat1.to_owned(); } - if ratio == I32F32::from_num(1) { + if ratio == I32F32::saturating_from_num(1) { return mat2.to_owned(); } assert!(mat1.len() == mat2.len()); @@ -1042,7 +1052,10 @@ pub fn interpolate(mat1: &[Vec<I32F32>], mat2: &[Vec<I32F32>], ratio: I32F32) -> return vec![vec![]; 1]; } let mut result: Vec<Vec<I32F32>> = - vec![vec![I32F32::from_num(0); mat1.first().unwrap_or(&vec![]).len()]; mat1.len()]; + vec![ + vec![I32F32::saturating_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() { @@ -1065,15 +1078,15 @@ pub fn interpolate_sparse( columns: u16, ratio: I32F32, ) -> Vec<Vec<(u16, I32F32)>> { - if ratio == I32F32::from_num(0) { + if ratio == I32F32::saturating_from_num(0) { return mat1.to_owned(); } - if ratio == I32F32::from_num(1) { + if ratio == I32F32::saturating_from_num(1) { return mat2.to_owned(); } assert!(mat1.len() == mat2.len()); let rows = mat1.len(); - let zero: I32F32 = I32F32::from_num(0); + let zero: I32F32 = I32F32::saturating_from_num(0); let mut result: Vec<Vec<(u16, I32F32)>> = vec![vec![]; rows]; for i in 0..rows { let mut row1: Vec<I32F32> = vec![zero; columns as usize]; @@ -1137,7 +1150,7 @@ pub fn hadamard_sparse( ) -> Vec<Vec<(u16, I32F32)>> { assert!(mat1.len() == mat2.len()); let rows = mat1.len(); - let zero: I32F32 = I32F32::from_num(0); + let zero: I32F32 = I32F32::saturating_from_num(0); let mut result: Vec<Vec<(u16, I32F32)>> = vec![vec![]; rows]; for i in 0..rows { let mut row1: Vec<I32F32> = vec![zero; columns as usize]; @@ -1169,7 +1182,7 @@ pub fn mat_ema(new: &[Vec<I32F32>], old: &[Vec<I32F32>], alpha: I32F32) -> Vec<V if first_row.is_empty() { return vec![vec![]; 1]; } - let one_minus_alpha: I32F32 = I32F32::from_num(1.0).saturating_sub(alpha); + let one_minus_alpha: I32F32 = I32F32::saturating_from_num(1.0).saturating_sub(alpha); new.iter() .zip(old) .map(|(new_row, old_row)| { @@ -1197,8 +1210,8 @@ pub fn mat_ema_sparse( ) -> Vec<Vec<(u16, I32F32)>> { assert!(new.len() == old.len()); let n = new.len(); // assume square matrix, rows=cols - let zero: I32F32 = I32F32::from_num(0.0); - let one_minus_alpha: I32F32 = I32F32::from_num(1.0).saturating_sub(alpha); + let zero: I32F32 = I32F32::saturating_from_num(0.0); + let one_minus_alpha: I32F32 = I32F32::saturating_from_num(1.0).saturating_sub(alpha); let mut result: Vec<Vec<(u16, I32F32)>> = vec![vec![]; n]; for i in 0..new.len() { let mut row: Vec<I32F32> = vec![zero; n]; @@ -1241,7 +1254,7 @@ pub fn mat_ema_alpha_vec_sparse( // Ensure the new and old matrices have the same number of rows. assert!(new.len() == old.len()); let n = new.len(); // Assume square matrix, rows=cols - let zero: I32F32 = I32F32::from_num(0.0); + let zero: I32F32 = I32F32::saturating_from_num(0.0); let mut result: Vec<Vec<(u16, I32F32)>> = vec![vec![]; n]; // Iterate over each row of the matrices. @@ -1273,7 +1286,8 @@ pub fn mat_ema_alpha_vec_sparse( // Retrieve the alpha value for the current column. let alpha_val: I32F32 = alpha.get(*j as usize).copied().unwrap_or(zero); // Calculate the complement of the alpha value using saturating subtraction. - let one_minus_alpha: I32F32 = I32F32::from_num(1.0).saturating_sub(alpha_val); + let one_minus_alpha: I32F32 = + I32F32::saturating_from_num(1.0).saturating_sub(alpha_val); // Compute the EMA component for the old value and add it to the row using saturating operations. if let Some(row_val) = row.get_mut(*j as usize) { *row_val = row_val.saturating_add(one_minus_alpha.saturating_mul(*value)); @@ -1323,7 +1337,10 @@ pub fn mat_ema_alpha_vec( // Initialize the result matrix with zeros, having the same dimensions as the new matrix. let mut result: Vec<Vec<I32F32>> = - vec![vec![I32F32::from_num(0.0); new.first().map_or(0, |row| row.len())]; new.len()]; + vec![ + vec![I32F32::saturating_from_num(0.0); new.first().map_or(0, |row| row.len())]; + new.len() + ]; // Iterate over each row of the matrices. for (i, (new_row, old_row)) in new.iter().zip(old).enumerate() { @@ -1333,7 +1350,7 @@ pub fn mat_ema_alpha_vec( // Iterate over each column of the current row. for (j, &alpha_val) in alpha.iter().enumerate().take(new_row.len()) { // Calculate the complement of the alpha value using saturating subtraction. - let one_minus_alpha = I32F32::from_num(1.0).saturating_sub(alpha_val); + let one_minus_alpha = I32F32::saturating_from_num(1.0).saturating_sub(alpha_val); // Compute the EMA for the current element using saturating operations. if let (Some(new_val), Some(old_val), Some(result_val)) = ( @@ -1365,7 +1382,7 @@ pub fn quantile(data: &[I32F32], quantile: f64) -> I32F32 { // If the data is empty, return 0 as the quantile value. if len == 0 { - return I32F32::from_num(0); + return I32F32::saturating_from_num(0); } // Calculate the position in the sorted array corresponding to the quantile. @@ -1382,20 +1399,20 @@ pub fn quantile(data: &[I32F32], quantile: f64) -> I32F32 { sorted_data .get(low) .copied() - .unwrap_or_else(|| I32F32::from_num(0)) + .unwrap_or_else(|| I32F32::saturating_from_num(0)) } else { // Otherwise, perform linear interpolation between the low and high values. let low_value = sorted_data .get(low) .copied() - .unwrap_or_else(|| I32F32::from_num(0)); + .unwrap_or_else(|| I32F32::saturating_from_num(0)); let high_value = sorted_data .get(high) .copied() - .unwrap_or_else(|| I32F32::from_num(0)); + .unwrap_or_else(|| I32F32::saturating_from_num(0)); // Calculate the weight for interpolation. - let weight = I32F32::from_num(pos - low as f64); + let weight = I32F32::saturating_from_num(pos - low as f64); // Return the interpolated value using saturating operations. low_value.saturating_add((high_value.saturating_sub(low_value)).saturating_mul(weight)) @@ -1404,10 +1421,10 @@ pub fn quantile(data: &[I32F32], quantile: f64) -> I32F32 { /// Safe ln function, returns 0 if value is 0. pub fn safe_ln(value: I32F32) -> I32F32 { - ln(value).unwrap_or(I32F32::from_num(0.0)) + ln(value).unwrap_or(I32F32::saturating_from_num(0.0)) } /// Safe exp function, returns 0 if value is 0. pub fn safe_exp(value: I32F32) -> I32F32 { - exp(value).unwrap_or(I32F32::from_num(0.0)) + exp(value).unwrap_or(I32F32::saturating_from_num(0.0)) } diff --git a/pallets/subtensor/src/epoch/run_epoch.rs b/pallets/subtensor/src/epoch/run_epoch.rs index 4e9f36e4e..9a7e4f6ab 100644 --- a/pallets/subtensor/src/epoch/run_epoch.rs +++ b/pallets/subtensor/src/epoch/run_epoch.rs @@ -231,11 +231,11 @@ impl<T: Config> Pallet<T> { } // Compute rao based emission scores. range: I96F32(0, rao_emission) - let float_rao_emission: I96F32 = I96F32::from_num(rao_emission); + let float_rao_emission: I96F32 = I96F32::saturating_from_num(rao_emission); let server_emission: Vec<I96F32> = normalized_server_emission .iter() - .map(|se: &I32F32| I96F32::from_num(*se).saturating_mul(float_rao_emission)) + .map(|se: &I32F32| I96F32::saturating_from_num(*se).saturating_mul(float_rao_emission)) .collect(); let server_emission: Vec<u64> = server_emission .iter() @@ -244,7 +244,7 @@ impl<T: Config> Pallet<T> { let validator_emission: Vec<I96F32> = normalized_validator_emission .iter() - .map(|ve: &I32F32| I96F32::from_num(*ve).saturating_mul(float_rao_emission)) + .map(|ve: &I32F32| I96F32::saturating_from_num(*ve).saturating_mul(float_rao_emission)) .collect(); let validator_emission: Vec<u64> = validator_emission .iter() @@ -254,7 +254,7 @@ impl<T: Config> Pallet<T> { // Used only to track combined emission in the storage. let combined_emission: Vec<I96F32> = normalized_combined_emission .iter() - .map(|ce: &I32F32| I96F32::from_num(*ce).saturating_mul(float_rao_emission)) + .map(|ce: &I32F32| I96F32::saturating_from_num(*ce).saturating_mul(float_rao_emission)) .collect(); let combined_emission: Vec<u64> = combined_emission .iter() @@ -602,11 +602,11 @@ impl<T: Config> Pallet<T> { } // Compute rao based emission scores. range: I96F32(0, rao_emission) - let float_rao_emission: I96F32 = I96F32::from_num(rao_emission); + let float_rao_emission: I96F32 = I96F32::saturating_from_num(rao_emission); let server_emission: Vec<I96F32> = normalized_server_emission .iter() - .map(|se: &I32F32| I96F32::from_num(*se).saturating_mul(float_rao_emission)) + .map(|se: &I32F32| I96F32::saturating_from_num(*se).saturating_mul(float_rao_emission)) .collect(); let server_emission: Vec<u64> = server_emission .iter() @@ -615,7 +615,7 @@ impl<T: Config> Pallet<T> { let validator_emission: Vec<I96F32> = normalized_validator_emission .iter() - .map(|ve: &I32F32| I96F32::from_num(*ve).saturating_mul(float_rao_emission)) + .map(|ve: &I32F32| I96F32::saturating_from_num(*ve).saturating_mul(float_rao_emission)) .collect(); let validator_emission: Vec<u64> = validator_emission .iter() @@ -625,7 +625,7 @@ impl<T: Config> Pallet<T> { // Only used to track emission in storage. let combined_emission: Vec<I96F32> = normalized_combined_emission .iter() - .map(|ce: &I32F32| I96F32::from_num(*ce).saturating_mul(float_rao_emission)) + .map(|ce: &I32F32| I96F32::saturating_from_num(*ce).saturating_mul(float_rao_emission)) .collect(); let combined_emission: Vec<u64> = combined_emission .iter() @@ -733,13 +733,15 @@ impl<T: Config> Pallet<T> { } pub fn get_float_rho(netuid: u16) -> I32F32 { - I32F32::from_num(Self::get_rho(netuid)) + I32F32::saturating_from_num(Self::get_rho(netuid)) } pub fn get_float_kappa(netuid: u16) -> I32F32 { - I32F32::from_num(Self::get_kappa(netuid)).safe_div(I32F32::from_num(u16::MAX)) + I32F32::saturating_from_num(Self::get_kappa(netuid)) + .safe_div(I32F32::saturating_from_num(u16::MAX)) } pub fn get_float_bonds_penalty(netuid: u16) -> I32F32 { - I32F32::from_num(Self::get_bonds_penalty(netuid)).safe_div(I32F32::from_num(u16::MAX)) + I32F32::saturating_from_num(Self::get_bonds_penalty(netuid)) + .safe_div(I32F32::saturating_from_num(u16::MAX)) } pub fn get_block_at_registration(netuid: u16) -> Vec<u64> { @@ -768,7 +770,7 @@ impl<T: Config> Pallet<T> { weights .get_mut(uid_i as usize) .expect("uid_i is filtered to be less than n; qed") - .push((*uid_j, I32F32::from_num(*weight_ij))); + .push((*uid_j, I32F32::saturating_from_num(*weight_ij))); } } weights @@ -777,7 +779,7 @@ impl<T: Config> Pallet<T> { /// Output unnormalized weights in [n, n] matrix, input weights are assumed to be row max-upscaled in u16. pub fn get_weights(netuid: u16) -> Vec<Vec<I32F32>> { let n: usize = Self::get_subnetwork_n(netuid) as usize; - let mut weights: Vec<Vec<I32F32>> = vec![vec![I32F32::from_num(0.0); n]; n]; + let mut weights: Vec<Vec<I32F32>> = vec![vec![I32F32::saturating_from_num(0.0); n]; n]; for (uid_i, weights_vec) in <Weights<T> as IterableStorageDoubleMap<u16, u16, Vec<(u16, u16)>>>::iter_prefix(netuid) .filter(|(uid_i, _)| *uid_i < n as u16) @@ -791,7 +793,7 @@ impl<T: Config> Pallet<T> { .expect("uid_i is filtered to be less than n; qed") .get_mut(uid_j as usize) .expect("uid_j is filtered to be less than n; qed") = - I32F32::from_num(weight_ij); + I32F32::saturating_from_num(weight_ij); } } weights @@ -809,7 +811,7 @@ impl<T: Config> Pallet<T> { bonds .get_mut(uid_i as usize) .expect("uid_i is filtered to be less than n; qed") - .push((uid_j, I32F32::from_num(bonds_ij))); + .push((uid_j, I32F32::saturating_from_num(bonds_ij))); } } bonds @@ -818,7 +820,7 @@ impl<T: Config> Pallet<T> { /// Output unnormalized bonds in [n, n] matrix, input bonds are assumed to be column max-upscaled in u16. pub fn get_bonds(netuid: u16) -> Vec<Vec<I32F32>> { let n: usize = Self::get_subnetwork_n(netuid) as usize; - let mut bonds: Vec<Vec<I32F32>> = vec![vec![I32F32::from_num(0.0); n]; n]; + let mut bonds: Vec<Vec<I32F32>> = vec![vec![I32F32::saturating_from_num(0.0); n]; n]; for (uid_i, bonds_vec) in <Bonds<T> as IterableStorageDoubleMap<u16, u16, Vec<(u16, u16)>>>::iter_prefix(netuid) .filter(|(uid_i, _)| *uid_i < n as u16) @@ -829,7 +831,7 @@ impl<T: Config> Pallet<T> { .expect("uid_i has been filtered to be less than n; qed") .get_mut(uid_j as usize) .expect("uid_j has been filtered to be less than n; qed") = - I32F32::from_num(bonds_ij); + I32F32::saturating_from_num(bonds_ij); } } bonds @@ -859,16 +861,21 @@ impl<T: Config> Pallet<T> { // extra caution to ensure we never divide by zero if consensus_high <= consensus_low || alpha_low == 0 || alpha_high == 0 { // Return 0 for both 'a' and 'b' when consensus values are equal - return (I32F32::from_num(0.0), I32F32::from_num(0.0)); + return ( + I32F32::saturating_from_num(0.0), + I32F32::saturating_from_num(0.0), + ); } // Calculate the slope 'a' of the logistic function. // a = (ln((1 / alpha_high - 1)) - ln((1 / alpha_low - 1))) / (consensus_low - consensus_high) let a = (safe_ln( - (I32F32::from_num(1.0).safe_div(alpha_high)).saturating_sub(I32F32::from_num(1.0)), + (I32F32::saturating_from_num(1.0).safe_div(alpha_high)) + .saturating_sub(I32F32::saturating_from_num(1.0)), ) .saturating_sub(safe_ln( - (I32F32::from_num(1.0).safe_div(alpha_low)).saturating_sub(I32F32::from_num(1.0)), + (I32F32::saturating_from_num(1.0).safe_div(alpha_low)) + .saturating_sub(I32F32::saturating_from_num(1.0)), ))) .safe_div(consensus_low.saturating_sub(consensus_high)); log::trace!("a: {:?}", a); @@ -876,7 +883,8 @@ impl<T: Config> Pallet<T> { // Calculate the intercept 'b' of the logistic function. // b = ln((1 / alpha_low - 1)) + a * consensus_low let b = safe_ln( - (I32F32::from_num(1.0).safe_div(alpha_low)).saturating_sub(I32F32::from_num(1.0)), + (I32F32::saturating_from_num(1.0).safe_div(alpha_low)) + .saturating_sub(I32F32::saturating_from_num(1.0)), ) .saturating_add(a.saturating_mul(consensus_low)); log::trace!("b: {:?}", b); @@ -905,7 +913,8 @@ impl<T: Config> Pallet<T> { // Compute the alpha value using the logistic function formula. // alpha = 1 / (1 + exp_val) - I32F32::from_num(1.0).safe_div(I32F32::from_num(1.0).saturating_add(exp_val)) + I32F32::saturating_from_num(1.0) + .safe_div(I32F32::saturating_from_num(1.0).saturating_add(exp_val)) }) .collect(); @@ -1019,13 +1028,14 @@ impl<T: Config> Pallet<T> { netuid: u16, ) -> Vec<Vec<(u16, I32F32)>> { // Retrieve the bonds moving average for the given network ID and scale it down. - let bonds_moving_average: I64F64 = I64F64::from_num(Self::get_bonds_moving_average(netuid)) - .safe_div(I64F64::from_num(1_000_000)); + let bonds_moving_average: I64F64 = + I64F64::saturating_from_num(Self::get_bonds_moving_average(netuid)) + .safe_div(I64F64::saturating_from_num(1_000_000)); // Calculate the alpha value for the EMA calculation. // Alpha is derived by subtracting the scaled bonds moving average from 1. - let alpha: I32F32 = - I32F32::from_num(1).saturating_sub(I32F32::from_num(bonds_moving_average)); + let alpha: I32F32 = I32F32::saturating_from_num(1) + .saturating_sub(I32F32::saturating_from_num(bonds_moving_average)); // Compute the Exponential Moving Average (EMA) of bonds using the calculated alpha value. let ema_bonds = mat_ema_sparse(bonds_delta, bonds, alpha); @@ -1052,13 +1062,14 @@ impl<T: Config> Pallet<T> { netuid: u16, ) -> Vec<Vec<I32F32>> { // Retrieve the bonds moving average for the given network ID and scale it down. - let bonds_moving_average: I64F64 = I64F64::from_num(Self::get_bonds_moving_average(netuid)) - .safe_div(I64F64::from_num(1_000_000)); + let bonds_moving_average: I64F64 = + I64F64::saturating_from_num(Self::get_bonds_moving_average(netuid)) + .safe_div(I64F64::saturating_from_num(1_000_000)); // Calculate the alpha value for the EMA calculation. // Alpha is derived by subtracting the scaled bonds moving average from 1. - let alpha: I32F32 = - I32F32::from_num(1).saturating_sub(I32F32::from_num(bonds_moving_average)); + let alpha: I32F32 = I32F32::saturating_from_num(1) + .saturating_sub(I32F32::saturating_from_num(bonds_moving_average)); // Compute the Exponential Moving Average (EMA) of bonds using the calculated alpha value. let ema_bonds = mat_ema(bonds_delta, bonds, alpha); @@ -1090,7 +1101,9 @@ impl<T: Config> Pallet<T> { // This way we avoid the quantil function panic. if LiquidAlphaOn::<T>::get(netuid) && !consensus.is_empty() - && consensus.iter().any(|&c| c != I32F32::from_num(0)) + && consensus + .iter() + .any(|&c| c != I32F32::saturating_from_num(0)) { // Calculate the 75th percentile (high) and 25th percentile (low) of the consensus values. let consensus_high = quantile(&consensus, 0.75); @@ -1158,7 +1171,9 @@ impl<T: Config> Pallet<T> { // Check if Liquid Alpha is enabled, consensus is not empty, and contains non-zero values. if LiquidAlphaOn::<T>::get(netuid) && !consensus.is_empty() - && consensus.iter().any(|&c| c != I32F32::from_num(0)) + && consensus + .iter() + .any(|&c| c != I32F32::saturating_from_num(0)) { // Calculate the 75th percentile (high) and 25th percentile (low) of the consensus values. let consensus_high = quantile(&consensus, 0.75); diff --git a/pallets/subtensor/src/lib.rs b/pallets/subtensor/src/lib.rs index cbe394037..3fd67e0d8 100644 --- a/pallets/subtensor/src/lib.rs +++ b/pallets/subtensor/src/lib.rs @@ -730,7 +730,7 @@ pub mod pallet { #[pallet::type_value] /// Default value for Share Pool variables pub fn DefaultSharePoolZero<T: Config>() -> U64F64 { - U64F64::from_num(0) + U64F64::saturating_from_num(0) } #[pallet::storage] diff --git a/pallets/subtensor/src/macros/genesis.rs b/pallets/subtensor/src/macros/genesis.rs index 96c4db62e..f1288c5a6 100644 --- a/pallets/subtensor/src/macros/genesis.rs +++ b/pallets/subtensor/src/macros/genesis.rs @@ -66,13 +66,13 @@ mod genesis { Alpha::<T>::insert( // Lock the initial funds making this key the owner. (hotkey.clone(), hotkey.clone(), netuid), - U64F64::from_num(1_000_000_000), + U64F64::saturating_from_num(1_000_000_000), ); TotalHotkeyAlpha::<T>::insert(hotkey.clone(), netuid, 1_000_000_000); TotalHotkeyShares::<T>::insert( hotkey.clone(), netuid, - U64F64::from_num(1_000_000_000), + U64F64::saturating_from_num(1_000_000_000), ); // TotalColdkeyAlpha::<T>::insert(hotkey.clone(), netuid, 1_000_000_000); SubnetAlphaOut::<T>::insert(netuid, 1_000_000_000); diff --git a/pallets/subtensor/src/migrations/migrate_rao.rs b/pallets/subtensor/src/migrations/migrate_rao.rs index 2b325aadc..ef4a9fecb 100644 --- a/pallets/subtensor/src/migrations/migrate_rao.rs +++ b/pallets/subtensor/src/migrations/migrate_rao.rs @@ -45,10 +45,10 @@ pub fn migrate_rao<T: Config>() -> Weight { }); // Set all the stake on root 0 subnet. Alpha::<T>::mutate((hotkey.clone(), coldkey.clone(), 0), |total| { - *total = total.saturating_add(U64F64::from_num(stake)) + *total = total.saturating_add(U64F64::saturating_from_num(stake)) }); TotalHotkeyShares::<T>::mutate(hotkey.clone(), 0, |total| { - *total = total.saturating_add(U64F64::from_num(stake)) + *total = total.saturating_add(U64F64::saturating_from_num(stake)) }); // Set the total stake on the hotkey TotalHotkeyAlpha::<T>::mutate(hotkey.clone(), 0, |total| { diff --git a/pallets/subtensor/src/rpc_info/delegate_info.rs b/pallets/subtensor/src/rpc_info/delegate_info.rs index 0f79ab2eb..f8d641823 100644 --- a/pallets/subtensor/src/rpc_info/delegate_info.rs +++ b/pallets/subtensor/src/rpc_info/delegate_info.rs @@ -28,16 +28,16 @@ impl<T: Config> Pallet<T> { emissions_per_day: U64F64, ) -> U64F64 { // Get the take as a percentage and subtract it from 1 for remainder. - let without_take: U64F64 = - U64F64::from_num(1).saturating_sub(U64F64::from_num(take.0).safe_div(u16::MAX.into())); + let without_take: U64F64 = U64F64::saturating_from_num(1) + .saturating_sub(U64F64::saturating_from_num(take.0).safe_div(u16::MAX.into())); - if total_stake > U64F64::from_num(0) { + if total_stake > U64F64::saturating_from_num(0) { emissions_per_day .saturating_mul(without_take) // Divide by 1000 TAO for return per 1k - .safe_div(total_stake.safe_div(U64F64::from_num(1000.0 * 1e9))) + .safe_div(total_stake.safe_div(U64F64::saturating_from_num(1000.0 * 1e9))) } else { - U64F64::from_num(0) + U64F64::saturating_from_num(0) } } @@ -67,7 +67,7 @@ impl<T: Config> Pallet<T> { let registrations = Self::get_registered_networks_for_hotkey(&delegate.clone()); let mut validator_permits = Vec::<Compact<u16>>::new(); - let mut emissions_per_day: U64F64 = U64F64::from_num(0); + let mut emissions_per_day: U64F64 = U64F64::saturating_from_num(0); for netuid in registrations.iter() { if let Ok(uid) = Self::get_uid_for_net_and_hotkey(*netuid, &delegate.clone()) { @@ -78,8 +78,8 @@ impl<T: Config> Pallet<T> { let emission: U64F64 = Self::get_emission_for_uid(*netuid, uid).into(); let tempo: U64F64 = Self::get_tempo(*netuid).into(); - if tempo > U64F64::from_num(0) { - let epochs_per_day: U64F64 = U64F64::from_num(7200).safe_div(tempo); + if tempo > U64F64::saturating_from_num(0) { + let epochs_per_day: U64F64 = U64F64::saturating_from_num(7200).safe_div(tempo); emissions_per_day = emissions_per_day.saturating_add(emission.saturating_mul(epochs_per_day)); } diff --git a/pallets/subtensor/src/staking/helpers.rs b/pallets/subtensor/src/staking/helpers.rs index 5bb10e0bb..0cbe3bccf 100644 --- a/pallets/subtensor/src/staking/helpers.rs +++ b/pallets/subtensor/src/staking/helpers.rs @@ -46,8 +46,9 @@ impl<T: Config> Pallet<T> { Self::get_all_subnet_netuids() .iter() .map(|netuid| { - let alpha: I96F32 = - I96F32::from_num(Self::get_stake_for_hotkey_on_subnet(hotkey, *netuid)); + let alpha: I96F32 = I96F32::saturating_from_num( + Self::get_stake_for_hotkey_on_subnet(hotkey, *netuid), + ); let tao_price: I96F32 = Self::get_alpha_price(*netuid); alpha.saturating_mul(tao_price).saturating_to_num::<u64>() }) @@ -65,7 +66,7 @@ impl<T: Config> Pallet<T> { for (netuid, alpha) in Alpha::<T>::iter_prefix((hotkey, coldkey)) { let tao_price: I96F32 = Self::get_alpha_price(netuid); total_stake = total_stake.saturating_add( - I96F32::from_num(alpha) + I96F32::saturating_from_num(alpha) .saturating_mul(tao_price) .saturating_to_num::<u64>(), ); diff --git a/pallets/subtensor/src/staking/stake_utils.rs b/pallets/subtensor/src/staking/stake_utils.rs index 482b7c163..1bd6aa1af 100644 --- a/pallets/subtensor/src/staking/stake_utils.rs +++ b/pallets/subtensor/src/staking/stake_utils.rs @@ -32,17 +32,17 @@ impl<T: Config> Pallet<T> { /// * `I96F32` - The price of alpha for the specified subnet. pub fn get_alpha_price(netuid: u16) -> I96F32 { if netuid == Self::get_root_netuid() { - return I96F32::from_num(1.0); // Root. + return I96F32::saturating_from_num(1.0); // Root. } if SubnetMechanism::<T>::get(netuid) == 0 { - return I96F32::from_num(1.0); // Stable + return I96F32::saturating_from_num(1.0); // Stable } if SubnetAlphaIn::<T>::get(netuid) == 0 { - I96F32::from_num(0) + I96F32::saturating_from_num(0) } else { - I96F32::from_num(SubnetTAO::<T>::get(netuid)) - .checked_div(I96F32::from_num(SubnetAlphaIn::<T>::get(netuid))) - .unwrap_or(I96F32::from_num(0)) + I96F32::saturating_from_num(SubnetTAO::<T>::get(netuid)) + .checked_div(I96F32::saturating_from_num(SubnetAlphaIn::<T>::get(netuid))) + .unwrap_or(I96F32::saturating_from_num(0)) } } @@ -67,11 +67,11 @@ impl<T: Config> Pallet<T> { let stored_weight = TaoWeight::<T>::get(); // Step 2: Convert the u64 weight to I96F32 - let weight_fixed = I96F32::from_num(stored_weight); + let weight_fixed = I96F32::saturating_from_num(stored_weight); // Step 3: Normalize the weight by dividing by u64::MAX // This ensures the result is always between 0 and 1 - weight_fixed.safe_div(I96F32::from_num(u64::MAX)) + weight_fixed.safe_div(I96F32::saturating_from_num(u64::MAX)) } /// Sets the global global weight in storage. @@ -102,16 +102,17 @@ impl<T: Config> Pallet<T> { netuid: u16, ) -> (I64F64, I64F64, I64F64) { // Retrieve the global tao weight. - let tao_weight = I64F64::from_num(Self::get_tao_weight()); + let tao_weight = I64F64::saturating_from_num(Self::get_tao_weight()); log::debug!("tao_weight: {:?}", tao_weight); // Step 1: Get stake of hotkey (neuron) let alpha_stake = - I64F64::from_num(Self::get_inherited_for_hotkey_on_subnet(hotkey, netuid)); + I64F64::saturating_from_num(Self::get_inherited_for_hotkey_on_subnet(hotkey, netuid)); log::trace!("alpha_stake: {:?}", alpha_stake); // Step 2: Get the global tao stake for the hotkey - let tao_stake = I64F64::from_num(Self::get_inherited_for_hotkey_on_subnet(hotkey, 0)); + let tao_stake = + I64F64::saturating_from_num(Self::get_inherited_for_hotkey_on_subnet(hotkey, 0)); log::trace!("tao_stake: {:?}", tao_stake); // Step 3: Combine alpha and tao stakes @@ -125,7 +126,7 @@ impl<T: Config> Pallet<T> { /// pub fn get_stake_weights_for_network(netuid: u16) -> (Vec<I64F64>, Vec<I64F64>, Vec<I64F64>) { // Retrieve the global tao weight. - let tao_weight: I64F64 = I64F64::from_num(Self::get_tao_weight()); + let tao_weight: I64F64 = I64F64::saturating_from_num(Self::get_tao_weight()); log::debug!("tao_weight: {:?}", tao_weight); // Step 1: Get subnetwork size @@ -136,9 +137,11 @@ impl<T: Config> Pallet<T> { .map(|uid| { if Keys::<T>::contains_key(netuid, uid) { let hotkey: T::AccountId = Keys::<T>::get(netuid, uid); - I64F64::from_num(Self::get_inherited_for_hotkey_on_subnet(&hotkey, netuid)) + I64F64::saturating_from_num(Self::get_inherited_for_hotkey_on_subnet( + &hotkey, netuid, + )) } else { - I64F64::from_num(0) + I64F64::saturating_from_num(0) } }) .collect(); @@ -150,9 +153,11 @@ impl<T: Config> Pallet<T> { .map(|uid| { if Keys::<T>::contains_key(netuid, uid) { let hotkey: T::AccountId = Keys::<T>::get(netuid, uid); - I64F64::from_num(Self::get_inherited_for_hotkey_on_subnet(&hotkey, 0)) + I64F64::saturating_from_num(Self::get_inherited_for_hotkey_on_subnet( + &hotkey, 0, + )) } else { - I64F64::from_num(0) + I64F64::saturating_from_num(0) } }) .collect(); @@ -200,7 +205,7 @@ impl<T: Config> Pallet<T> { pub fn get_inherited_for_hotkey_on_subnet(hotkey: &T::AccountId, netuid: u16) -> u64 { // Step 1: Retrieve the initial total stake (alpha) for the hotkey on the specified subnet. let initial_alpha: I96F32 = - I96F32::from_num(Self::get_stake_for_hotkey_on_subnet(hotkey, netuid)); + I96F32::saturating_from_num(Self::get_stake_for_hotkey_on_subnet(hotkey, netuid)); log::trace!( "Initial alpha for hotkey {:?} on subnet {}: {:?}", hotkey, @@ -212,8 +217,8 @@ impl<T: Config> Pallet<T> { } // Initialize variables to track alpha allocated to children and inherited from parents. - let mut alpha_to_children: I96F32 = I96F32::from_num(0); - let mut alpha_from_parents: I96F32 = I96F32::from_num(0); + let mut alpha_to_children: I96F32 = I96F32::saturating_from_num(0); + let mut alpha_from_parents: I96F32 = I96F32::saturating_from_num(0); // Step 2: Retrieve the lists of parents and children for the hotkey on the subnet. let parents: Vec<(u64, T::AccountId)> = Self::get_parents(hotkey, netuid); @@ -234,8 +239,8 @@ impl<T: Config> Pallet<T> { // Step 3: Calculate the total alpha allocated to children. for (proportion, _) in children { // Convert the proportion to a normalized value between 0 and 1. - let normalized_proportion: I96F32 = - I96F32::from_num(proportion).safe_div(I96F32::from_num(u64::MAX)); + let normalized_proportion: I96F32 = I96F32::saturating_from_num(proportion) + .safe_div(I96F32::saturating_from_num(u64::MAX)); log::trace!( "Normalized proportion for child: {:?}", normalized_proportion @@ -243,7 +248,7 @@ impl<T: Config> Pallet<T> { // Calculate the amount of alpha to be allocated to this child. let alpha_proportion_to_child: I96F32 = - I96F32::from_num(initial_alpha).saturating_mul(normalized_proportion); + I96F32::saturating_from_num(initial_alpha).saturating_mul(normalized_proportion); log::trace!("Alpha proportion to child: {:?}", alpha_proportion_to_child); // Add this child's allocation to the total alpha allocated to children. @@ -255,7 +260,7 @@ impl<T: Config> Pallet<T> { for (proportion, parent) in parents { // Retrieve the parent's total stake on this subnet. let parent_alpha: I96F32 = - I96F32::from_num(Self::get_stake_for_hotkey_on_subnet(&parent, netuid)); + I96F32::saturating_from_num(Self::get_stake_for_hotkey_on_subnet(&parent, netuid)); log::trace!( "Parent alpha for parent {:?} on subnet {}: {:?}", parent, @@ -264,8 +269,8 @@ impl<T: Config> Pallet<T> { ); // Convert the proportion to a normalized value between 0 and 1. - let normalized_proportion: I96F32 = - I96F32::from_num(proportion).safe_div(I96F32::from_num(u64::MAX)); + let normalized_proportion: I96F32 = I96F32::saturating_from_num(proportion) + .safe_div(I96F32::saturating_from_num(u64::MAX)); log::trace!( "Normalized proportion from parent: {:?}", normalized_proportion @@ -273,7 +278,7 @@ impl<T: Config> Pallet<T> { // Calculate the amount of alpha to be inherited from this parent. let alpha_proportion_from_parent: I96F32 = - I96F32::from_num(parent_alpha).saturating_mul(normalized_proportion); + I96F32::saturating_from_num(parent_alpha).saturating_mul(normalized_proportion); log::trace!( "Alpha proportion from parent: {:?}", alpha_proportion_from_parent @@ -461,19 +466,20 @@ impl<T: Config> Pallet<T> { // Step 2: Initialized vars. let alpha: I96F32 = if mechanism_id == 1 { // Step 3.a.1: Dynamic mechanism calculations - let tao_reserves: I96F32 = I96F32::from_num(SubnetTAO::<T>::get(netuid)); - let alpha_reserves: I96F32 = I96F32::from_num(SubnetAlphaIn::<T>::get(netuid)); + let tao_reserves: I96F32 = I96F32::saturating_from_num(SubnetTAO::<T>::get(netuid)); + let alpha_reserves: I96F32 = + I96F32::saturating_from_num(SubnetAlphaIn::<T>::get(netuid)); // Step 3.a.2: Compute constant product k = alpha * tao let k: I96F32 = alpha_reserves.saturating_mul(tao_reserves); // Step 3.a.3: Calculate alpha staked using the constant product formula // alpha_stake_recieved = current_alpha - (k / (current_tao + new_tao)) alpha_reserves.saturating_sub( - k.checked_div(tao_reserves.saturating_add(I96F32::from_num(tao))) - .unwrap_or(I96F32::from_num(0)), + k.checked_div(tao_reserves.saturating_add(I96F32::saturating_from_num(tao))) + .unwrap_or(I96F32::saturating_from_num(0)), ) } else { // Step 3.b.1: Stable mechanism, just return the value 1:1 - I96F32::from_num(tao) + I96F32::saturating_from_num(tao) }; // Return simulated amount. alpha.saturating_to_num::<u64>() @@ -488,19 +494,20 @@ impl<T: Config> Pallet<T> { // Step 2: Swap alpha and attain tao let tao: I96F32 = if mechanism_id == 1 { // Step 3.a.1: Dynamic mechanism calculations - let tao_reserves: I96F32 = I96F32::from_num(SubnetTAO::<T>::get(netuid)); - let alpha_reserves: I96F32 = I96F32::from_num(SubnetAlphaIn::<T>::get(netuid)); + let tao_reserves: I96F32 = I96F32::saturating_from_num(SubnetTAO::<T>::get(netuid)); + let alpha_reserves: I96F32 = + I96F32::saturating_from_num(SubnetAlphaIn::<T>::get(netuid)); // Step 3.a.2: Compute constant product k = alpha * tao let k: I96F32 = alpha_reserves.saturating_mul(tao_reserves); // Step 3.a.3: Calculate alpha staked using the constant product formula // tao_recieved = tao_reserves - (k / (alpha_reserves + new_tao)) tao_reserves.saturating_sub( - k.checked_div(alpha_reserves.saturating_add(I96F32::from_num(alpha))) - .unwrap_or(I96F32::from_num(0)), + k.checked_div(alpha_reserves.saturating_add(I96F32::saturating_from_num(alpha))) + .unwrap_or(I96F32::saturating_from_num(0)), ) } else { // Step 3.b.1: Stable mechanism, just return the value 1:1 - I96F32::from_num(alpha) + I96F32::saturating_from_num(alpha) }; tao.saturating_to_num::<u64>() } @@ -514,19 +521,20 @@ impl<T: Config> Pallet<T> { // Step 2: Initialized vars. let alpha: I96F32 = if mechanism_id == 1 { // Step 3.a.1: Dynamic mechanism calculations - let tao_reserves: I96F32 = I96F32::from_num(SubnetTAO::<T>::get(netuid)); - let alpha_reserves: I96F32 = I96F32::from_num(SubnetAlphaIn::<T>::get(netuid)); + let tao_reserves: I96F32 = I96F32::saturating_from_num(SubnetTAO::<T>::get(netuid)); + let alpha_reserves: I96F32 = + I96F32::saturating_from_num(SubnetAlphaIn::<T>::get(netuid)); // Step 3.a.2: Compute constant product k = alpha * tao let k: I96F32 = alpha_reserves.saturating_mul(tao_reserves); // Step 3.a.3: Calculate alpha staked using the constant product formula // alpha_stake_recieved = current_alpha - (k / (current_tao + new_tao)) alpha_reserves.saturating_sub( - k.checked_div(tao_reserves.saturating_add(I96F32::from_num(tao))) - .unwrap_or(I96F32::from_num(0)), + k.checked_div(tao_reserves.saturating_add(I96F32::saturating_from_num(tao))) + .unwrap_or(I96F32::saturating_from_num(0)), ) } else { // Step 3.b.1: Stable mechanism, just return the value 1:1 - I96F32::from_num(tao) + I96F32::saturating_from_num(tao) }; // Step 4. Decrease Alpha reserves. SubnetAlphaIn::<T>::mutate(netuid, |total| { @@ -561,19 +569,20 @@ impl<T: Config> Pallet<T> { // Step 2: Swap alpha and attain tao let tao: I96F32 = if mechanism_id == 1 { // Step 3.a.1: Dynamic mechanism calculations - let tao_reserves: I96F32 = I96F32::from_num(SubnetTAO::<T>::get(netuid)); - let alpha_reserves: I96F32 = I96F32::from_num(SubnetAlphaIn::<T>::get(netuid)); + let tao_reserves: I96F32 = I96F32::saturating_from_num(SubnetTAO::<T>::get(netuid)); + let alpha_reserves: I96F32 = + I96F32::saturating_from_num(SubnetAlphaIn::<T>::get(netuid)); // Step 3.a.2: Compute constant product k = alpha * tao let k: I96F32 = alpha_reserves.saturating_mul(tao_reserves); // Step 3.a.3: Calculate alpha staked using the constant product formula // tao_recieved = tao_reserves - (k / (alpha_reserves + new_tao)) tao_reserves.saturating_sub( - k.checked_div(alpha_reserves.saturating_add(I96F32::from_num(alpha))) - .unwrap_or(I96F32::from_num(0)), + k.checked_div(alpha_reserves.saturating_add(I96F32::saturating_from_num(alpha))) + .unwrap_or(I96F32::saturating_from_num(0)), ) } else { // Step 3.b.1: Stable mechanism, just return the value 1:1 - I96F32::from_num(alpha) + I96F32::saturating_from_num(alpha) }; // Step 4: Increase Alpha reserves. SubnetAlphaIn::<T>::mutate(netuid, |total| { @@ -871,7 +880,7 @@ impl<T: Config> SharePoolDataOperations<AlphaShareKey<T>> for HotkeyAlphaSharePoolDataOperations<T> { fn get_shared_value(&self) -> U64F64 { - U64F64::from_num(crate::TotalHotkeyAlpha::<T>::get(&self.hotkey, self.netuid)) + U64F64::saturating_from_num(crate::TotalHotkeyAlpha::<T>::get(&self.hotkey, self.netuid)) } fn get_share(&self, key: &AlphaShareKey<T>) -> U64F64 { diff --git a/pallets/subtensor/src/tests/math.rs b/pallets/subtensor/src/tests/math.rs index 7409ed8eb..59d826ab3 100644 --- a/pallets/subtensor/src/tests/math.rs +++ b/pallets/subtensor/src/tests/math.rs @@ -2305,10 +2305,9 @@ fn test_math_fixed64_to_fixed32() { } #[test] -#[should_panic(expected = "overflow")] -fn test_math_fixed64_to_fixed32_panics() { +fn test_math_fixed64_to_fixed32_saturates() { let bad_input = I64F64::from_num(u32::MAX); - fixed64_to_fixed32(bad_input); + assert_eq!(fixed64_to_fixed32(bad_input), I32F32::max_value()); } #[test] @@ -2362,10 +2361,9 @@ fn test_vec_fixed64_to_fixed32() { } #[test] -#[should_panic(expected = "overflow")] -fn test_vec_fixed64_to_fixed32_panics() { +fn test_vec_fixed64_to_fixed32_saturates() { let bad_input = vec![I64F64::from_num(i64::MAX)]; - vec_fixed64_to_fixed32(bad_input); + assert_eq!(vec_fixed64_to_fixed32(bad_input), [I32F32::max_value()]); } #[test] diff --git a/pallets/subtensor/src/utils/misc.rs b/pallets/subtensor/src/utils/misc.rs index 4749d6594..b27921f27 100644 --- a/pallets/subtensor/src/utils/misc.rs +++ b/pallets/subtensor/src/utils/misc.rs @@ -593,7 +593,8 @@ impl<T: Config> Pallet<T> { SubnetOwnerCut::<T>::get() } pub fn get_float_subnet_owner_cut() -> I96F32 { - I96F32::from_num(SubnetOwnerCut::<T>::get()).safe_div(I96F32::from_num(u16::MAX)) + I96F32::saturating_from_num(SubnetOwnerCut::<T>::get()) + .safe_div(I96F32::saturating_from_num(u16::MAX)) } pub fn set_subnet_owner_cut(subnet_owner_cut: u16) { SubnetOwnerCut::<T>::set(subnet_owner_cut); @@ -665,8 +666,10 @@ impl<T: Config> Pallet<T> { pub fn get_alpha_values_32(netuid: u16) -> (I32F32, I32F32) { let (alpha_low, alpha_high): (u16, u16) = AlphaValues::<T>::get(netuid); - let converted_low = I32F32::from_num(alpha_low).safe_div(I32F32::from_num(u16::MAX)); - let converted_high = I32F32::from_num(alpha_high).safe_div(I32F32::from_num(u16::MAX)); + let converted_low = + I32F32::saturating_from_num(alpha_low).safe_div(I32F32::saturating_from_num(u16::MAX)); + let converted_high = + I32F32::saturating_from_num(alpha_high).safe_div(I32F32::saturating_from_num(u16::MAX)); (converted_low, converted_high) } diff --git a/primitives/share-pool/src/lib.rs b/primitives/share-pool/src/lib.rs index 8af506945..f3e00fca9 100644 --- a/primitives/share-pool/src/lib.rs +++ b/primitives/share-pool/src/lib.rs @@ -52,7 +52,7 @@ where shared_value .checked_div(denominator) - .unwrap_or(U64F64::from_num(0)) + .unwrap_or(U64F64::saturating_from_num(0)) .saturating_mul(current_share) .saturating_to_num::<u64>() } @@ -69,9 +69,9 @@ where pub fn update_value_for_all(&mut self, update: i64) { let shared_value: U64F64 = self.state_ops.get_shared_value(); self.state_ops.set_shared_value(if update >= 0 { - shared_value.saturating_add(U64F64::from_num(update)) + shared_value.saturating_add(U64F64::saturating_from_num(update)) } else { - shared_value.saturating_sub(U64F64::from_num(update.neg())) + shared_value.saturating_sub(U64F64::saturating_from_num(update.neg())) }); } @@ -92,31 +92,33 @@ where self.state_ops.set_share(key, new_shared_value); } else { // There are already keys in the pool, set or update this key - let value_per_share: I64F64 = I64F64::from_num( + let value_per_share: I64F64 = I64F64::saturating_from_num( shared_value .checked_div(denominator) // denominator is never 0 here - .unwrap_or(U64F64::from_num(0)), + .unwrap_or(U64F64::saturating_from_num(0)), ); - let shares_per_update: I64F64 = I64F64::from_num(update) + let shares_per_update: I64F64 = I64F64::saturating_from_num(update) .checked_div(value_per_share) - .unwrap_or(I64F64::from_num(0)); + .unwrap_or(I64F64::saturating_from_num(0)); if shares_per_update >= 0 { self.state_ops.set_denominator( - denominator.saturating_add(U64F64::from_num(shares_per_update)), + denominator.saturating_add(U64F64::saturating_from_num(shares_per_update)), ); self.state_ops.set_share( key, - current_share.saturating_add(U64F64::from_num(shares_per_update)), + current_share.saturating_add(U64F64::saturating_from_num(shares_per_update)), ); } else { self.state_ops.set_denominator( - denominator.saturating_sub(U64F64::from_num(shares_per_update.neg())), + denominator + .saturating_sub(U64F64::saturating_from_num(shares_per_update.neg())), ); self.state_ops.set_share( key, - current_share.saturating_sub(U64F64::from_num(shares_per_update.neg())), + current_share + .saturating_sub(U64F64::saturating_from_num(shares_per_update.neg())), ); } } @@ -137,9 +139,9 @@ mod tests { impl MockSharePoolDataOperations { fn new() -> Self { MockSharePoolDataOperations { - shared_value: U64F64::from_num(0), + shared_value: U64F64::saturating_from_num(0), share: BTreeMap::new(), - denominator: U64F64::from_num(0), + denominator: U64F64::saturating_from_num(0), } } } @@ -150,7 +152,10 @@ mod tests { } fn get_share(&self, key: &u16) -> U64F64 { - *self.share.get(key).unwrap_or(&U64F64::from_num(0)) + *self + .share + .get(key) + .unwrap_or(&U64F64::saturating_from_num(0)) } fn try_get_share(&self, key: &u16) -> Result<U64F64, ()> { @@ -180,10 +185,10 @@ mod tests { #[test] fn test_get_value() { let mut mock_ops = MockSharePoolDataOperations::new(); - mock_ops.set_denominator(U64F64::from_num(10)); - mock_ops.set_share(&1_u16, U64F64::from_num(3)); - mock_ops.set_share(&2_u16, U64F64::from_num(7)); - mock_ops.set_shared_value(U64F64::from_num(100)); + mock_ops.set_denominator(U64F64::saturating_from_num(10)); + mock_ops.set_share(&1_u16, U64F64::saturating_from_num(3)); + mock_ops.set_share(&2_u16, U64F64::saturating_from_num(7)); + mock_ops.set_shared_value(U64F64::saturating_from_num(100)); let share_pool = SharePool::new(mock_ops); let result1 = share_pool.get_value(&1); let result2 = share_pool.get_value(&2); @@ -194,7 +199,7 @@ mod tests { #[test] fn test_division_by_zero() { let mut mock_ops = MockSharePoolDataOperations::new(); - mock_ops.set_denominator(U64F64::from_num(0)); // Zero denominator + mock_ops.set_denominator(U64F64::saturating_from_num(0)); // Zero denominator let pool = SharePool::<u16, MockSharePoolDataOperations>::new(mock_ops); let value = pool.get_value(&1); @@ -204,10 +209,10 @@ mod tests { #[test] fn test_max_shared_value() { let mut mock_ops = MockSharePoolDataOperations::new(); - mock_ops.set_shared_value(U64F64::from_num(u64::MAX)); - mock_ops.set_share(&1, U64F64::from_num(3)); // Use a neutral value for share - mock_ops.set_share(&2, U64F64::from_num(7)); // Use a neutral value for share - mock_ops.set_denominator(U64F64::from_num(10)); // Neutral value to see max effect + mock_ops.set_shared_value(U64F64::saturating_from_num(u64::MAX)); + mock_ops.set_share(&1, U64F64::saturating_from_num(3)); // Use a neutral value for share + mock_ops.set_share(&2, U64F64::saturating_from_num(7)); // Use a neutral value for share + mock_ops.set_denominator(U64F64::saturating_from_num(10)); // Neutral value to see max effect let pool = SharePool::<u16, MockSharePoolDataOperations>::new(mock_ops); let max_value = pool.get_value(&1) + pool.get_value(&2); @@ -217,10 +222,10 @@ mod tests { #[test] fn test_max_share_value() { let mut mock_ops = MockSharePoolDataOperations::new(); - mock_ops.set_shared_value(U64F64::from_num(1_000_000_000)); // Use a neutral value for shared value - mock_ops.set_share(&1, U64F64::from_num(u64::MAX / 2)); - mock_ops.set_share(&2, U64F64::from_num(u64::MAX / 2)); - mock_ops.set_denominator(U64F64::from_num(u64::MAX)); + mock_ops.set_shared_value(U64F64::saturating_from_num(1_000_000_000)); // Use a neutral value for shared value + mock_ops.set_share(&1, U64F64::saturating_from_num(u64::MAX / 2)); + mock_ops.set_share(&2, U64F64::saturating_from_num(u64::MAX / 2)); + mock_ops.set_denominator(U64F64::saturating_from_num(u64::MAX)); let pool = SharePool::<u16, MockSharePoolDataOperations>::new(mock_ops); let value1 = pool.get_value(&1) as i128; @@ -268,6 +273,9 @@ mod tests { let mut pool = SharePool::<u16, MockSharePoolDataOperations>::new(mock_ops); pool.update_value_for_all(1000); - assert_eq!(pool.state_ops.shared_value, U64F64::from_num(1000)); + assert_eq!( + pool.state_ops.shared_value, + U64F64::saturating_from_num(1000) + ); } } From 72f35e0a478a0e4a3c448545358475f63a2426d3 Mon Sep 17 00:00:00 2001 From: Greg Zaitsev <gregz@opentensor.dev> Date: Mon, 27 Jan 2025 15:45:39 -0500 Subject: [PATCH 096/145] Fix zepter --- primitives/safe-math/Cargo.toml | 1 + primitives/share-pool/Cargo.toml | 1 + 2 files changed, 2 insertions(+) diff --git a/primitives/safe-math/Cargo.toml b/primitives/safe-math/Cargo.toml index f50ac6b90..f67d53c5d 100644 --- a/primitives/safe-math/Cargo.toml +++ b/primitives/safe-math/Cargo.toml @@ -16,4 +16,5 @@ default = ["std"] std = [ "substrate-fixed/std", "sp-std/std", + "num-traits/std", ] diff --git a/primitives/share-pool/Cargo.toml b/primitives/share-pool/Cargo.toml index 370f32cbb..e0696ee30 100644 --- a/primitives/share-pool/Cargo.toml +++ b/primitives/share-pool/Cargo.toml @@ -16,4 +16,5 @@ default = ["std"] std = [ "substrate-fixed/std", "sp-std/std", + "safe-math/std", ] From ffa8a6e2a057e6bb6272fb31a1eff607703b2bfa Mon Sep 17 00:00:00 2001 From: Greg Zaitsev <gregz@opentensor.dev> Date: Mon, 27 Jan 2025 15:50:38 -0500 Subject: [PATCH 097/145] Fix zepter --- pallets/subtensor/Cargo.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/pallets/subtensor/Cargo.toml b/pallets/subtensor/Cargo.toml index e8085615d..f22f855fc 100644 --- a/pallets/subtensor/Cargo.toml +++ b/pallets/subtensor/Cargo.toml @@ -105,6 +105,7 @@ std = [ "ark-serialize/std", "w3f-bls/std", "rand_chacha/std", + "safe-math/std", "sha2/std", "share-pool/std" ] From 95d2d533b5beb6b0322f95a4a96c2db6e8182fd9 Mon Sep 17 00:00:00 2001 From: Greg Zaitsev <gregz@opentensor.dev> Date: Mon, 27 Jan 2025 18:30:09 -0500 Subject: [PATCH 098/145] Invalidate staking and unstaking transactions if liquidity is inadequate --- pallets/subtensor/src/lib.rs | 15 +- pallets/subtensor/src/macros/errors.rs | 2 + pallets/subtensor/src/staking/stake_utils.rs | 235 ++++++++++--------- 3 files changed, 136 insertions(+), 116 deletions(-) diff --git a/pallets/subtensor/src/lib.rs b/pallets/subtensor/src/lib.rs index 3fd67e0d8..c26a2a422 100644 --- a/pallets/subtensor/src/lib.rs +++ b/pallets/subtensor/src/lib.rs @@ -82,7 +82,7 @@ pub mod pallet { use sp_std::collections::vec_deque::VecDeque; use sp_std::vec; use sp_std::vec::Vec; - use substrate_fixed::types::U64F64; + use substrate_fixed::types::{I96F32, U64F64}; use subtensor_macros::freeze_struct; #[cfg(not(feature = "std"))] @@ -733,6 +733,13 @@ pub mod pallet { U64F64::saturating_from_num(0) } + #[pallet::type_value] + /// Default value for minimum liquidity in pool + pub fn DefaultMinimumPoolLiquidity<T: Config>() -> I96F32 { + // I96F32::saturating_from_num(1_000_000) + I96F32::saturating_from_num(0) + } + #[pallet::storage] pub type ColdkeySwapScheduleDuration<T: Config> = StorageValue<_, BlockNumberFor<T>, ValueQuery, DefaultColdkeySwapScheduleDuration<T>>; @@ -1562,6 +1569,7 @@ pub enum CustomTransactionError { HotkeyAccountDoesntExist, NotEnoughStakeToWithdraw, RateLimitExceeded, + InsufficientLiquidity, BadRequest, } @@ -1575,6 +1583,7 @@ impl From<CustomTransactionError> for u8 { CustomTransactionError::HotkeyAccountDoesntExist => 4, CustomTransactionError::NotEnoughStakeToWithdraw => 5, CustomTransactionError::RateLimitExceeded => 6, + CustomTransactionError::InsufficientLiquidity => 7, CustomTransactionError::BadRequest => 255, } } @@ -1642,6 +1651,10 @@ where CustomTransactionError::NotEnoughStakeToWithdraw.into(), ) .into()), + Error::<T>::InsufficientLiquidity => Err(InvalidTransaction::Custom( + CustomTransactionError::InsufficientLiquidity.into(), + ) + .into()), _ => Err( InvalidTransaction::Custom(CustomTransactionError::BadRequest.into()).into(), ), diff --git a/pallets/subtensor/src/macros/errors.rs b/pallets/subtensor/src/macros/errors.rs index dd5e97b78..f1eea3b9c 100644 --- a/pallets/subtensor/src/macros/errors.rs +++ b/pallets/subtensor/src/macros/errors.rs @@ -185,5 +185,7 @@ mod errors { CommittingWeightsTooFast, /// Stake amount is too low. AmountTooLow, + /// Not enough liquidity. + InsufficientLiquidity, } } diff --git a/pallets/subtensor/src/staking/stake_utils.rs b/pallets/subtensor/src/staking/stake_utils.rs index 1bd6aa1af..284d484da 100644 --- a/pallets/subtensor/src/staking/stake_utils.rs +++ b/pallets/subtensor/src/staking/stake_utils.rs @@ -457,155 +457,148 @@ impl<T: Config> Pallet<T> { } } - /// Swaps TAO for the alpha token on the subnet. + /// Calculates Some(Alpha) returned from pool by staking operation + /// if liquidity allows that. If not, returns None. /// - /// Updates TaoIn, AlphaIn, and AlphaOut - pub fn sim_swap_tao_for_alpha(netuid: u16, tao: u64) -> u64 { + /// If new alpha_reserve is about to drop below DefaultMinimumPoolLiquidity, + /// then don't do it. + /// + pub fn sim_swap_tao_for_alpha(netuid: u16, tao: u64) -> Option<u64> { // Step 1: Get the mechanism type for the subnet (0 for Stable, 1 for Dynamic) let mechanism_id: u16 = SubnetMechanism::<T>::get(netuid); // Step 2: Initialized vars. - let alpha: I96F32 = if mechanism_id == 1 { + if mechanism_id == 1 { // Step 3.a.1: Dynamic mechanism calculations let tao_reserves: I96F32 = I96F32::saturating_from_num(SubnetTAO::<T>::get(netuid)); let alpha_reserves: I96F32 = I96F32::saturating_from_num(SubnetAlphaIn::<T>::get(netuid)); // Step 3.a.2: Compute constant product k = alpha * tao let k: I96F32 = alpha_reserves.saturating_mul(tao_reserves); + + // Calculate new alpha reserve + let new_alpha_reserves: I96F32 = k + .checked_div(tao_reserves.saturating_add(I96F32::saturating_from_num(tao))) + .unwrap_or(I96F32::saturating_from_num(0)); + // Step 3.a.3: Calculate alpha staked using the constant product formula // alpha_stake_recieved = current_alpha - (k / (current_tao + new_tao)) - alpha_reserves.saturating_sub( - k.checked_div(tao_reserves.saturating_add(I96F32::saturating_from_num(tao))) - .unwrap_or(I96F32::saturating_from_num(0)), - ) + if new_alpha_reserves >= DefaultMinimumPoolLiquidity::<T>::get() { + Some( + alpha_reserves + .saturating_sub(new_alpha_reserves) + .saturating_to_num::<u64>(), + ) + } else { + None + } } else { // Step 3.b.1: Stable mechanism, just return the value 1:1 - I96F32::saturating_from_num(tao) - }; - // Return simulated amount. - alpha.saturating_to_num::<u64>() + Some(tao) + } } - /// Swaps a subnet's Alpba token for TAO. + /// Calculates Some(Tao) returned from pool by unstaking operation + /// if liquidity allows that. If not, returns None. /// - /// Updates TaoIn, AlphaIn, and AlphaOut - pub fn sim_swap_alpha_for_tao(netuid: u16, alpha: u64) -> u64 { + /// If new tao_reserve is about to drop below DefaultMinimumPoolLiquidity, + /// then don't do it. + /// + pub fn sim_swap_alpha_for_tao(netuid: u16, alpha: u64) -> Option<u64> { // Step 1: Get the mechanism type for the subnet (0 for Stable, 1 for Dynamic) let mechanism_id: u16 = SubnetMechanism::<T>::get(netuid); // Step 2: Swap alpha and attain tao - let tao: I96F32 = if mechanism_id == 1 { + if mechanism_id == 1 { // Step 3.a.1: Dynamic mechanism calculations let tao_reserves: I96F32 = I96F32::saturating_from_num(SubnetTAO::<T>::get(netuid)); let alpha_reserves: I96F32 = I96F32::saturating_from_num(SubnetAlphaIn::<T>::get(netuid)); // Step 3.a.2: Compute constant product k = alpha * tao let k: I96F32 = alpha_reserves.saturating_mul(tao_reserves); + + // Calculate new tao reserve + let new_tao_reserves: I96F32 = k + .checked_div(alpha_reserves.saturating_add(I96F32::saturating_from_num(alpha))) + .unwrap_or(I96F32::saturating_from_num(0)); + // Step 3.a.3: Calculate alpha staked using the constant product formula // tao_recieved = tao_reserves - (k / (alpha_reserves + new_tao)) - tao_reserves.saturating_sub( - k.checked_div(alpha_reserves.saturating_add(I96F32::saturating_from_num(alpha))) - .unwrap_or(I96F32::saturating_from_num(0)), - ) + if new_tao_reserves >= DefaultMinimumPoolLiquidity::<T>::get() { + Some( + tao_reserves + .saturating_sub(new_tao_reserves) + .saturating_to_num::<u64>(), + ) + } else { + None + } } else { // Step 3.b.1: Stable mechanism, just return the value 1:1 - I96F32::saturating_from_num(alpha) - }; - tao.saturating_to_num::<u64>() + Some(alpha) + } } /// Swaps TAO for the alpha token on the subnet. /// /// Updates TaoIn, AlphaIn, and AlphaOut pub fn swap_tao_for_alpha(netuid: u16, tao: u64) -> u64 { - // Step 1: Get the mechanism type for the subnet (0 for Stable, 1 for Dynamic) - let mechanism_id: u16 = SubnetMechanism::<T>::get(netuid); - // Step 2: Initialized vars. - let alpha: I96F32 = if mechanism_id == 1 { - // Step 3.a.1: Dynamic mechanism calculations - let tao_reserves: I96F32 = I96F32::saturating_from_num(SubnetTAO::<T>::get(netuid)); - let alpha_reserves: I96F32 = - I96F32::saturating_from_num(SubnetAlphaIn::<T>::get(netuid)); - // Step 3.a.2: Compute constant product k = alpha * tao - let k: I96F32 = alpha_reserves.saturating_mul(tao_reserves); - // Step 3.a.3: Calculate alpha staked using the constant product formula - // alpha_stake_recieved = current_alpha - (k / (current_tao + new_tao)) - alpha_reserves.saturating_sub( - k.checked_div(tao_reserves.saturating_add(I96F32::saturating_from_num(tao))) - .unwrap_or(I96F32::saturating_from_num(0)), - ) + if let Some(alpha) = Self::sim_swap_tao_for_alpha(netuid, tao) { + // Step 4. Decrease Alpha reserves. + SubnetAlphaIn::<T>::mutate(netuid, |total| { + *total = total.saturating_sub(alpha); + }); + // Step 5: Increase Alpha outstanding. + SubnetAlphaOut::<T>::mutate(netuid, |total| { + *total = total.saturating_add(alpha); + }); + // Step 6: Increase Tao reserves. + SubnetTAO::<T>::mutate(netuid, |total| { + *total = total.saturating_add(tao); + }); + // Step 7: Increase Total Tao reserves. + TotalStake::<T>::mutate(|total| { + *total = total.saturating_add(tao); + }); + // Step 8. Decrease Alpha reserves. + SubnetVolume::<T>::mutate(netuid, |total| { + *total = total.saturating_sub(tao); + }); + // Step 9. Return the alpha received. + alpha } else { - // Step 3.b.1: Stable mechanism, just return the value 1:1 - I96F32::saturating_from_num(tao) - }; - // Step 4. Decrease Alpha reserves. - SubnetAlphaIn::<T>::mutate(netuid, |total| { - *total = total.saturating_sub(alpha.saturating_to_num::<u64>()); - }); - // Step 5: Increase Alpha outstanding. - SubnetAlphaOut::<T>::mutate(netuid, |total| { - *total = total.saturating_add(alpha.saturating_to_num::<u64>()); - }); - // Step 6: Increase Tao reserves. - SubnetTAO::<T>::mutate(netuid, |total| { - *total = total.saturating_add(tao); - }); - // Step 7: Increase Total Tao reserves. - TotalStake::<T>::mutate(|total| { - *total = total.saturating_add(tao); - }); - // Step 8. Decrease Alpha reserves. - SubnetVolume::<T>::mutate(netuid, |total| { - *total = total.saturating_sub(tao); - }); - // Step 9. Return the alpha received. - alpha.saturating_to_num::<u64>() + 0 + } } /// Swaps a subnet's Alpba token for TAO. /// /// Updates TaoIn, AlphaIn, and AlphaOut pub fn swap_alpha_for_tao(netuid: u16, alpha: u64) -> u64 { - // Step 1: Get the mechanism type for the subnet (0 for Stable, 1 for Dynamic) - let mechanism_id: u16 = SubnetMechanism::<T>::get(netuid); - // Step 2: Swap alpha and attain tao - let tao: I96F32 = if mechanism_id == 1 { - // Step 3.a.1: Dynamic mechanism calculations - let tao_reserves: I96F32 = I96F32::saturating_from_num(SubnetTAO::<T>::get(netuid)); - let alpha_reserves: I96F32 = - I96F32::saturating_from_num(SubnetAlphaIn::<T>::get(netuid)); - // Step 3.a.2: Compute constant product k = alpha * tao - let k: I96F32 = alpha_reserves.saturating_mul(tao_reserves); - // Step 3.a.3: Calculate alpha staked using the constant product formula - // tao_recieved = tao_reserves - (k / (alpha_reserves + new_tao)) - tao_reserves.saturating_sub( - k.checked_div(alpha_reserves.saturating_add(I96F32::saturating_from_num(alpha))) - .unwrap_or(I96F32::saturating_from_num(0)), - ) + if let Some(tao) = Self::sim_swap_tao_for_alpha(netuid, alpha) { + // Step 4: Increase Alpha reserves. + SubnetAlphaIn::<T>::mutate(netuid, |total| { + *total = total.saturating_add(alpha); + }); + // Step 5: Decrease Alpha outstanding. + SubnetAlphaOut::<T>::mutate(netuid, |total| { + *total = total.saturating_sub(alpha); + }); + // Step 6: Decrease tao reserves. + SubnetTAO::<T>::mutate(netuid, |total| { + *total = total.saturating_sub(tao); + }); + // Step 7: Reduce total TAO reserves. + TotalStake::<T>::mutate(|total| { + *total = total.saturating_sub(tao); + }); + // Step 8. Decrease Alpha reserves. + SubnetVolume::<T>::mutate(netuid, |total| { + *total = total.saturating_sub(tao); + }); + // Step 9. Return the tao received. + tao } else { - // Step 3.b.1: Stable mechanism, just return the value 1:1 - I96F32::saturating_from_num(alpha) - }; - // Step 4: Increase Alpha reserves. - SubnetAlphaIn::<T>::mutate(netuid, |total| { - *total = total.saturating_add(alpha); - }); - // Step 5: Decrease Alpha outstanding. - SubnetAlphaOut::<T>::mutate(netuid, |total| { - *total = total.saturating_sub(alpha); - }); - // Step 6: Decrease tao reserves. - SubnetTAO::<T>::mutate(netuid, |total| { - *total = total.saturating_sub(tao.saturating_to_num::<u64>()); - }); - // Step 7: Reduce total TAO reserves. - TotalStake::<T>::mutate(|total| { - *total = total.saturating_sub(tao.saturating_to_num::<u64>()); - }); - // Step 8. Decrease Alpha reserves. - SubnetVolume::<T>::mutate(netuid, |total| { - *total = total.saturating_sub(tao.saturating_to_num::<u64>()); - }); - // Step 9. Return the tao received. - tao.saturating_to_num::<u64>() + 0 + } } /// Unstakes alpha from a subnet for a given hotkey and coldkey pair. @@ -759,6 +752,12 @@ impl<T: Config> Pallet<T> { Error::<T>::HotKeyAccountNotExists ); + // Ensure that we have adequate liquidity + ensure!( + Self::sim_swap_tao_for_alpha(netuid, stake_to_be_added).is_some(), + Error::<T>::InsufficientLiquidity + ); + Ok(()) } @@ -774,11 +773,14 @@ impl<T: Config> Pallet<T> { ensure!(Self::if_subnet_exist(netuid), Error::<T>::SubnetNotExists); // Ensure that the stake amount to be removed is above the minimum in tao equivalent. - let tao_equivalent = Self::sim_swap_alpha_for_tao(netuid, alpha_unstaked); - ensure!( - tao_equivalent > DefaultMinStake::<T>::get(), - Error::<T>::AmountTooLow - ); + if let Some(tao_equivalent) = Self::sim_swap_alpha_for_tao(netuid, alpha_unstaked) { + ensure!( + tao_equivalent > DefaultMinStake::<T>::get(), + Error::<T>::AmountTooLow + ); + } else { + return Err(Error::<T>::InsufficientLiquidity); + }; // Ensure that the hotkey account exists this is only possible through registration. ensure!( @@ -843,11 +845,14 @@ impl<T: Config> Pallet<T> { ); // Ensure that the stake amount to be removed is above the minimum in tao equivalent. - let tao_equivalent = Self::sim_swap_alpha_for_tao(origin_netuid, alpha_amount); - ensure!( - tao_equivalent > DefaultMinStake::<T>::get(), - Error::<T>::AmountTooLow - ); + if let Some(tao_equivalent) = Self::sim_swap_alpha_for_tao(origin_netuid, alpha_amount) { + ensure!( + tao_equivalent > DefaultMinStake::<T>::get(), + Error::<T>::AmountTooLow + ); + } else { + return Err(Error::<T>::InsufficientLiquidity); + } Ok(()) } From bbc542d82411425b4036ca03892acc926e104a9b Mon Sep 17 00:00:00 2001 From: Greg Zaitsev <gregz@opentensor.dev> Date: Mon, 27 Jan 2025 18:41:16 -0500 Subject: [PATCH 099/145] Fix swap_alpha_for_tao --- pallets/subtensor/src/staking/stake_utils.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pallets/subtensor/src/staking/stake_utils.rs b/pallets/subtensor/src/staking/stake_utils.rs index 284d484da..d68f2eb1c 100644 --- a/pallets/subtensor/src/staking/stake_utils.rs +++ b/pallets/subtensor/src/staking/stake_utils.rs @@ -573,7 +573,7 @@ impl<T: Config> Pallet<T> { /// /// Updates TaoIn, AlphaIn, and AlphaOut pub fn swap_alpha_for_tao(netuid: u16, alpha: u64) -> u64 { - if let Some(tao) = Self::sim_swap_tao_for_alpha(netuid, alpha) { + if let Some(tao) = Self::sim_swap_alpha_for_tao(netuid, alpha) { // Step 4: Increase Alpha reserves. SubnetAlphaIn::<T>::mutate(netuid, |total| { *total = total.saturating_add(alpha); From 680b5bcfef06ee2dee520e40175de46961b7ba98 Mon Sep 17 00:00:00 2001 From: Greg Zaitsev <gregz@opentensor.dev> Date: Mon, 27 Jan 2025 18:43:02 -0500 Subject: [PATCH 100/145] Set DefaultMinimumPoolLiquidity --- pallets/subtensor/src/lib.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/pallets/subtensor/src/lib.rs b/pallets/subtensor/src/lib.rs index c26a2a422..7f9cd7120 100644 --- a/pallets/subtensor/src/lib.rs +++ b/pallets/subtensor/src/lib.rs @@ -736,8 +736,7 @@ pub mod pallet { #[pallet::type_value] /// Default value for minimum liquidity in pool pub fn DefaultMinimumPoolLiquidity<T: Config>() -> I96F32 { - // I96F32::saturating_from_num(1_000_000) - I96F32::saturating_from_num(0) + I96F32::saturating_from_num(1_000_000) } #[pallet::storage] From 0401f08ae93fbf0d85cf8daf796b07b554a2eb4d Mon Sep 17 00:00:00 2001 From: Greg Zaitsev <gregz@opentensor.dev> Date: Tue, 28 Jan 2025 09:58:08 -0500 Subject: [PATCH 101/145] Fix test_do_move_max_values --- pallets/subtensor/src/tests/move_stake.rs | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/pallets/subtensor/src/tests/move_stake.rs b/pallets/subtensor/src/tests/move_stake.rs index f299832ed..4d48cfa6a 100644 --- a/pallets/subtensor/src/tests/move_stake.rs +++ b/pallets/subtensor/src/tests/move_stake.rs @@ -724,7 +724,7 @@ fn test_do_move_storage_updates() { // 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 +// SKIP_WASM_BUILD=1 RUST_LOG=debug cargo test --package pallet-subtensor --lib -- tests::move_stake::test_do_move_max_values --exact --show-output #[test] fn test_do_move_max_values() { new_test_ext(1).execute_with(|| { @@ -740,6 +740,11 @@ fn test_do_move_max_values() { // 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); + + // Add lots of liquidity to bypass low liquidity check + SubnetTAO::<Test>::insert(netuid, u64::MAX/1000); + SubnetAlphaIn::<Test>::insert(netuid, u64::MAX/1000); + SubtensorModule::stake_into_subnet(&origin_hotkey, &coldkey, netuid, max_stake, fee); let alpha = SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( &origin_hotkey, @@ -773,7 +778,7 @@ fn test_do_move_max_values() { netuid ), alpha, - epsilon = 5 + epsilon = alpha / 1_000_000 ); }); } From dd251428f0a44f63d84e78a97ade1b30953bfe27 Mon Sep 17 00:00:00 2001 From: Greg Zaitsev <gregz@opentensor.dev> Date: Tue, 28 Jan 2025 11:52:46 -0500 Subject: [PATCH 102/145] Add tests --- pallets/subtensor/src/tests/move_stake.rs | 4 +- pallets/subtensor/src/tests/staking.rs | 177 ++++++++++++++++++++++ 2 files changed, 179 insertions(+), 2 deletions(-) diff --git a/pallets/subtensor/src/tests/move_stake.rs b/pallets/subtensor/src/tests/move_stake.rs index 4d48cfa6a..7a60d4736 100644 --- a/pallets/subtensor/src/tests/move_stake.rs +++ b/pallets/subtensor/src/tests/move_stake.rs @@ -742,8 +742,8 @@ fn test_do_move_max_values() { SubtensorModule::create_account_if_non_existent(&coldkey, &destination_hotkey); // Add lots of liquidity to bypass low liquidity check - SubnetTAO::<Test>::insert(netuid, u64::MAX/1000); - SubnetAlphaIn::<Test>::insert(netuid, u64::MAX/1000); + SubnetTAO::<Test>::insert(netuid, u64::MAX / 1000); + SubnetAlphaIn::<Test>::insert(netuid, u64::MAX / 1000); SubtensorModule::stake_into_subnet(&origin_hotkey, &coldkey, netuid, max_stake, fee); let alpha = SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( diff --git a/pallets/subtensor/src/tests/staking.rs b/pallets/subtensor/src/tests/staking.rs index 54413fdc0..a570175c9 100644 --- a/pallets/subtensor/src/tests/staking.rs +++ b/pallets/subtensor/src/tests/staking.rs @@ -556,6 +556,78 @@ fn test_remove_stake_total_balance_no_change() { }); } +#[test] +fn test_add_stake_insufficient_liquidity() { + new_test_ext(1).execute_with(|| { + let subnet_owner_coldkey = U256::from(1001); + let subnet_owner_hotkey = U256::from(1002); + let hotkey = U256::from(2); + let coldkey = U256::from(3); + let amount_staked = DefaultMinStake::<Test>::get() * 10 + DefaultStakingFee::<Test>::get(); + + let netuid = add_dynamic_network(&subnet_owner_hotkey, &subnet_owner_coldkey); + SubtensorModule::create_account_if_non_existent(&coldkey, &hotkey); + SubtensorModule::add_balance_to_coldkey_account(&coldkey, amount_staked); + + // Set the liquidity at lowest possible value so that all staking requests fail + SubnetTAO::<Test>::insert( + netuid, + DefaultMinimumPoolLiquidity::<Test>::get().to_num::<u64>(), + ); + SubnetAlphaIn::<Test>::insert( + netuid, + DefaultMinimumPoolLiquidity::<Test>::get().to_num::<u64>(), + ); + + // Check the error + assert_noop!( + SubtensorModule::add_stake( + RuntimeOrigin::signed(coldkey), + hotkey, + netuid, + amount_staked + ), + Error::<Test>::InsufficientLiquidity + ); + }); +} + +#[test] +fn test_remove_stake_insufficient_liquidity() { + new_test_ext(1).execute_with(|| { + let subnet_owner_coldkey = U256::from(1001); + let subnet_owner_hotkey = U256::from(1002); + let hotkey = U256::from(2); + let coldkey = U256::from(3); + let amount_staked = DefaultMinStake::<Test>::get() * 10 + DefaultStakingFee::<Test>::get(); + + let netuid = add_dynamic_network(&subnet_owner_hotkey, &subnet_owner_coldkey); + SubtensorModule::create_account_if_non_existent(&coldkey, &hotkey); + SubtensorModule::add_balance_to_coldkey_account(&coldkey, amount_staked); + + // Simulate stake for hotkey + SubnetTAO::<Test>::insert(netuid, u64::MAX / 1000); + SubnetAlphaIn::<Test>::insert(netuid, u64::MAX / 1000); + let alpha = SubtensorModule::stake_into_subnet(&hotkey, &coldkey, netuid, amount_staked, 0); + + // Set the liquidity at lowest possible value so that all staking requests fail + SubnetTAO::<Test>::insert( + netuid, + DefaultMinimumPoolLiquidity::<Test>::get().to_num::<u64>(), + ); + SubnetAlphaIn::<Test>::insert( + netuid, + DefaultMinimumPoolLiquidity::<Test>::get().to_num::<u64>(), + ); + + // Check the error + assert_noop!( + SubtensorModule::remove_stake(RuntimeOrigin::signed(coldkey), hotkey, netuid, alpha), + Error::<Test>::InsufficientLiquidity + ); + }); +} + #[test] fn test_remove_stake_total_issuance_no_change() { // When we remove stake, the total issuance of the balances pallet should not change @@ -2132,3 +2204,108 @@ fn test_stake_below_min_validate() { assert_ok!(result_min_stake); }); } + +#[test] +fn test_stake_low_liquidity_validate() { + // Testing the signed extension validate function + // correctly filters the `add_stake` transaction. + + new_test_ext(0).execute_with(|| { + let subnet_owner_coldkey = U256::from(1001); + let subnet_owner_hotkey = U256::from(1002); + let hotkey = U256::from(2); + let coldkey = U256::from(3); + let amount_staked = DefaultMinStake::<Test>::get() * 10 + DefaultStakingFee::<Test>::get(); + + let netuid = add_dynamic_network(&subnet_owner_hotkey, &subnet_owner_coldkey); + SubtensorModule::create_account_if_non_existent(&coldkey, &hotkey); + SubtensorModule::add_balance_to_coldkey_account(&coldkey, amount_staked); + + // Set the liquidity at lowest possible value so that all staking requests fail + SubnetTAO::<Test>::insert( + netuid, + DefaultMinimumPoolLiquidity::<Test>::get().to_num::<u64>(), + ); + SubnetAlphaIn::<Test>::insert( + netuid, + DefaultMinimumPoolLiquidity::<Test>::get().to_num::<u64>(), + ); + + // Add stake call + let call = RuntimeCall::SubtensorModule(SubtensorCall::add_stake { + hotkey, + netuid, + amount_staked, + }); + + let info: crate::DispatchInfo = + crate::DispatchInfoOf::<<Test as frame_system::Config>::RuntimeCall>::default(); + + let extension = crate::SubtensorSignedExtension::<Test>::new(); + // Submit to the signed extension validate function + let result_no_stake = extension.validate(&coldkey, &call.clone(), &info, 10); + + // Should fail due to insufficient stake + assert_err!( + result_no_stake, + crate::TransactionValidityError::Invalid(crate::InvalidTransaction::Custom( + CustomTransactionError::InsufficientLiquidity.into() + )) + ); + }); +} + +#[test] +fn test_unstake_low_liquidity_validate() { + // Testing the signed extension validate function + // correctly filters the `add_stake` transaction. + + new_test_ext(0).execute_with(|| { + let subnet_owner_coldkey = U256::from(1001); + let subnet_owner_hotkey = U256::from(1002); + let hotkey = U256::from(2); + let coldkey = U256::from(3); + let amount_staked = DefaultMinStake::<Test>::get() * 10 + DefaultStakingFee::<Test>::get(); + + let netuid = add_dynamic_network(&subnet_owner_hotkey, &subnet_owner_coldkey); + SubtensorModule::create_account_if_non_existent(&coldkey, &hotkey); + SubtensorModule::add_balance_to_coldkey_account(&coldkey, amount_staked); + + // Simulate stake for hotkey + SubnetTAO::<Test>::insert(netuid, u64::MAX / 1000); + SubnetAlphaIn::<Test>::insert(netuid, u64::MAX / 1000); + let alpha = SubtensorModule::stake_into_subnet(&hotkey, &coldkey, netuid, amount_staked, 0); + + // Set the liquidity at lowest possible value so that all staking requests fail + SubnetTAO::<Test>::insert( + netuid, + DefaultMinimumPoolLiquidity::<Test>::get().to_num::<u64>(), + ); + SubnetAlphaIn::<Test>::insert( + netuid, + DefaultMinimumPoolLiquidity::<Test>::get().to_num::<u64>(), + ); + + // Remove stake call + let call = RuntimeCall::SubtensorModule(SubtensorCall::remove_stake { + hotkey, + netuid, + amount_unstaked: alpha, + }); + + let info: crate::DispatchInfo = + crate::DispatchInfoOf::<<Test as frame_system::Config>::RuntimeCall>::default(); + + let extension = crate::SubtensorSignedExtension::<Test>::new(); + // Submit to the signed extension validate function + let result_no_stake = extension.validate(&coldkey, &call.clone(), &info, 10); + + // Should fail due to insufficient stake + assert_err!( + result_no_stake, + crate::TransactionValidityError::Invalid(crate::InvalidTransaction::Custom( + CustomTransactionError::InsufficientLiquidity.into() + )) + ); + }); +} From 693221ce5c0d596d8cc20417d01b5f7e667a04c9 Mon Sep 17 00:00:00 2001 From: Greg Zaitsev <gregz@opentensor.dev> Date: Tue, 28 Jan 2025 12:11:14 -0500 Subject: [PATCH 103/145] Fix unsafe math --- pallets/subtensor/src/staking/add_stake.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pallets/subtensor/src/staking/add_stake.rs b/pallets/subtensor/src/staking/add_stake.rs index 2f0d4875f..1dc75664f 100644 --- a/pallets/subtensor/src/staking/add_stake.rs +++ b/pallets/subtensor/src/staking/add_stake.rs @@ -185,7 +185,7 @@ impl<T: Config> Pallet<T> { .unwrap_or(zero); U96F32::from_num(sqrt) - .saturating_sub(U96F32::from_num(tao_reserve_float)) - .to_num::<u64>() + .saturating_sub(U96F32::saturating_from_num(tao_reserve_float)) + .saturating_to_num::<u64>() } } From 624dd07e6a9e57c2267b816ee1cc47b81e312c27 Mon Sep 17 00:00:00 2001 From: gztensor <166415444+gztensor@users.noreply.github.com> Date: Tue, 28 Jan 2025 09:15:20 -0800 Subject: [PATCH 104/145] Update pallets/subtensor/src/staking/stake_utils.rs Co-authored-by: Cameron Fairchild <cameron@opentensor.ai> --- pallets/subtensor/src/staking/stake_utils.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pallets/subtensor/src/staking/stake_utils.rs b/pallets/subtensor/src/staking/stake_utils.rs index d68f2eb1c..1e6d2d0a1 100644 --- a/pallets/subtensor/src/staking/stake_utils.rs +++ b/pallets/subtensor/src/staking/stake_utils.rs @@ -560,7 +560,7 @@ impl<T: Config> Pallet<T> { }); // Step 8. Decrease Alpha reserves. SubnetVolume::<T>::mutate(netuid, |total| { - *total = total.saturating_sub(tao); + *total = total.saturating_add(tao); }); // Step 9. Return the alpha received. alpha From 7fc9b4179ff671e5d554bcb0553e99cd7bd9d726 Mon Sep 17 00:00:00 2001 From: gztensor <166415444+gztensor@users.noreply.github.com> Date: Tue, 28 Jan 2025 09:15:38 -0800 Subject: [PATCH 105/145] Update pallets/subtensor/src/staking/stake_utils.rs Co-authored-by: Cameron Fairchild <cameron@opentensor.ai> --- pallets/subtensor/src/staking/stake_utils.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pallets/subtensor/src/staking/stake_utils.rs b/pallets/subtensor/src/staking/stake_utils.rs index 1e6d2d0a1..805b61e50 100644 --- a/pallets/subtensor/src/staking/stake_utils.rs +++ b/pallets/subtensor/src/staking/stake_utils.rs @@ -592,7 +592,7 @@ impl<T: Config> Pallet<T> { }); // Step 8. Decrease Alpha reserves. SubnetVolume::<T>::mutate(netuid, |total| { - *total = total.saturating_sub(tao); + *total = total.saturating_add(tao); }); // Step 9. Return the tao received. tao From 7eaacf9b3d89350f29d30a54336d0ced3c9e30de Mon Sep 17 00:00:00 2001 From: Cameron Fairchild <cameron@opentensor.ai> Date: Tue, 28 Jan 2025 13:39:32 -0500 Subject: [PATCH 106/145] make min burn only callable by root --- pallets/admin-utils/src/lib.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pallets/admin-utils/src/lib.rs b/pallets/admin-utils/src/lib.rs index 7de39aa38..90e0c1292 100644 --- a/pallets/admin-utils/src/lib.rs +++ b/pallets/admin-utils/src/lib.rs @@ -554,7 +554,7 @@ pub mod pallet { } /// The extrinsic sets the minimum burn for a subnet. - /// It is only callable by the root account or subnet owner. + /// It is only callable by the root account. /// The extrinsic will call the Subtensor pallet to set the minimum burn. #[pallet::call_index(22)] #[pallet::weight(<T as Config>::WeightInfo::sudo_set_min_burn())] @@ -563,7 +563,7 @@ pub mod pallet { netuid: u16, min_burn: u64, ) -> DispatchResult { - pallet_subtensor::Pallet::<T>::ensure_subnet_owner_or_root(origin, netuid)?; + ensure_root(origin)?; ensure!( pallet_subtensor::Pallet::<T>::if_subnet_exist(netuid), From 4cf3a6851e87435acc1e12778ea093093973764e Mon Sep 17 00:00:00 2001 From: Cameron Fairchild <cameron@opentensor.ai> Date: Tue, 28 Jan 2025 13:39:48 -0500 Subject: [PATCH 107/145] add tests for reg disable -> tao-in == 0 --- pallets/subtensor/src/tests/coinbase.rs | 186 ++++++++++++++++++++++++ 1 file changed, 186 insertions(+) diff --git a/pallets/subtensor/src/tests/coinbase.rs b/pallets/subtensor/src/tests/coinbase.rs index ff4908172..fb7a927be 100644 --- a/pallets/subtensor/src/tests/coinbase.rs +++ b/pallets/subtensor/src/tests/coinbase.rs @@ -389,3 +389,189 @@ fn test_total_issuance_after_coinbase() { ); }); } + +// Verifies that the total issuance after the coinbase is not changed when registration is disabled. +// Includes TAO weight. +// SKIP_WASM_BUILD=1 RUST_LOG=debug cargo test --package pallet-subtensor --lib -- tests::coinbase::test_registration_disabled_total_issuance_same --exact --show-output --nocapture +#[test] +fn test_registration_disabled_total_issuance_same() { + new_test_ext(1).execute_with(|| { + let netuid: u16 = 1; + add_network(netuid, 1, 0); + // Set TAO weight to 18% + SubtensorModule::set_tao_weight(I96F32::from_num(0.18).saturating_to_num::<u64>()); + // Set owner cut to ~11.11% + SubtensorModule::set_subnet_owner_cut(u16::MAX / 9); + let total_coinbase_emission: I96F32 = I96F32::from_num(1_123_456_789); + let epsilon: u64 = 100; + + // Define hotkeys and coldkeys + let hotkey_a: U256 = U256::from(1); + let hotkey_b: U256 = U256::from(2); + let hotkey_c: U256 = U256::from(3); + let coldkey_a: U256 = U256::from(100); + let coldkey_b: U256 = U256::from(101); + let coldkey_c: U256 = U256::from(102); + + // Register neurons with decreasing stakes + register_ok_neuron(netuid, hotkey_a, coldkey_a, 0); + register_ok_neuron(netuid, hotkey_b, coldkey_b, 0); + register_ok_neuron(netuid, hotkey_c, coldkey_c, 0); + + // Add initial stakes + SubtensorModule::add_balance_to_coldkey_account(&coldkey_a, 1_000); + SubtensorModule::add_balance_to_coldkey_account(&coldkey_b, 1_000); + SubtensorModule::add_balance_to_coldkey_account(&coldkey_c, 1_000); + + // Swap to alpha + let total_tao: I96F32 = I96F32::from_num(300_000 + 100_000 + 50_000); + let total_alpha: I96F32 = I96F32::from_num(SubtensorModule::swap_tao_for_alpha( + netuid, + total_tao.saturating_to_num::<u64>(), + )); + + // Set the stakes directly + // This avoids needing to swap tao to alpha, impacting the initial stake distribution. + SubtensorModule::increase_stake_for_hotkey_and_coldkey_on_subnet( + &hotkey_a, + &coldkey_a, + netuid, + (total_alpha * I96F32::from_num(300_000) / total_tao).saturating_to_num::<u64>(), + ); + SubtensorModule::increase_stake_for_hotkey_and_coldkey_on_subnet( + &hotkey_b, + &coldkey_b, + netuid, + (total_alpha * I96F32::from_num(100_000) / total_tao).saturating_to_num::<u64>(), + ); + SubtensorModule::increase_stake_for_hotkey_and_coldkey_on_subnet( + &hotkey_c, + &coldkey_c, + netuid, + (total_alpha * I96F32::from_num(50_000) / total_tao).saturating_to_num::<u64>(), + ); + + // Stake some to root + let stake_to_root: u64 = 10_000_000; + SubtensorModule::add_balance_to_coldkey_account(&coldkey_a, stake_to_root); + SubtensorModule::increase_stake_for_hotkey_and_coldkey_on_subnet( + &hotkey_a, + &coldkey_a, + netuid, + stake_to_root, + ); + + let alpha_price = SubtensorModule::get_alpha_price(netuid); + log::info!("alpha_price: {:?}", alpha_price); + + // Get the total issuance + let mut total_issuance_before = TotalIssuance::<Test>::get(); + log::info!("total_issuance_before: {:?}", total_issuance_before); + + // Disable registration on the network + SubtensorModule::set_network_registration_allowed(netuid, false); + SubtensorModule::set_network_pow_registration_allowed(netuid, false); + + // Run the coinbase + SubtensorModule::run_coinbase(total_coinbase_emission); + + // Should be the same + let total_issuance_after = TotalIssuance::<Test>::get(); + assert_abs_diff_eq!( + total_issuance_after, + total_issuance_before, + epsilon = epsilon + ); + }); +} + +// Verifies that the TAO-in after the coinbase is not changed when registration is disabled. +// Includes TAO weight. +// SKIP_WASM_BUILD=1 RUST_LOG=debug cargo test --package pallet-subtensor --lib -- tests::coinbase::test_registration_disabled_tao_in_same --exact --show-output --nocapture +#[test] +fn test_registration_disabled_tao_in_same() { + new_test_ext(1).execute_with(|| { + let netuid: u16 = 1; + add_network(netuid, 1, 0); + // Set TAO weight to 18% + SubtensorModule::set_tao_weight(I96F32::from_num(0.18).saturating_to_num::<u64>()); + // Set owner cut to ~11.11% + SubtensorModule::set_subnet_owner_cut(u16::MAX / 9); + let total_coinbase_emission: I96F32 = I96F32::from_num(1_123_456_789); + let epsilon: u64 = 100; + + // Define hotkeys and coldkeys + let hotkey_a: U256 = U256::from(1); + let hotkey_b: U256 = U256::from(2); + let hotkey_c: U256 = U256::from(3); + let coldkey_a: U256 = U256::from(100); + let coldkey_b: U256 = U256::from(101); + let coldkey_c: U256 = U256::from(102); + + // Register neurons with decreasing stakes + register_ok_neuron(netuid, hotkey_a, coldkey_a, 0); + register_ok_neuron(netuid, hotkey_b, coldkey_b, 0); + register_ok_neuron(netuid, hotkey_c, coldkey_c, 0); + + // Add initial stakes + SubtensorModule::add_balance_to_coldkey_account(&coldkey_a, 1_000); + SubtensorModule::add_balance_to_coldkey_account(&coldkey_b, 1_000); + SubtensorModule::add_balance_to_coldkey_account(&coldkey_c, 1_000); + + // Swap to alpha + let total_tao: I96F32 = I96F32::from_num(300_000 + 100_000 + 50_000); + let total_alpha: I96F32 = I96F32::from_num(SubtensorModule::swap_tao_for_alpha( + netuid, + total_tao.saturating_to_num::<u64>(), + )); + + // Set the stakes directly + // This avoids needing to swap tao to alpha, impacting the initial stake distribution. + SubtensorModule::increase_stake_for_hotkey_and_coldkey_on_subnet( + &hotkey_a, + &coldkey_a, + netuid, + (total_alpha * I96F32::from_num(300_000) / total_tao).saturating_to_num::<u64>(), + ); + SubtensorModule::increase_stake_for_hotkey_and_coldkey_on_subnet( + &hotkey_b, + &coldkey_b, + netuid, + (total_alpha * I96F32::from_num(100_000) / total_tao).saturating_to_num::<u64>(), + ); + SubtensorModule::increase_stake_for_hotkey_and_coldkey_on_subnet( + &hotkey_c, + &coldkey_c, + netuid, + (total_alpha * I96F32::from_num(50_000) / total_tao).saturating_to_num::<u64>(), + ); + + // Stake some to root + let stake_to_root: u64 = 10_000_000; + SubtensorModule::add_balance_to_coldkey_account(&coldkey_a, stake_to_root); + SubtensorModule::increase_stake_for_hotkey_and_coldkey_on_subnet( + &hotkey_a, + &coldkey_a, + netuid, + stake_to_root, + ); + + let alpha_price = SubtensorModule::get_alpha_price(netuid); + log::info!("alpha_price: {:?}", alpha_price); + + // Get the total issuance + let mut tao_in_before = SubnetTAO::<Test>::get(netuid); + log::info!("tao_in_before: {:?}", tao_in_before); + + // Disable registration on the network + SubtensorModule::set_network_registration_allowed(netuid, false); + SubtensorModule::set_network_pow_registration_allowed(netuid, false); + + // Run the coinbase + SubtensorModule::run_coinbase(total_coinbase_emission); + + // Should be the same + let tao_in_after = SubnetTAO::<Test>::get(netuid); + assert_abs_diff_eq!(tao_in_after, tao_in_before, epsilon = epsilon); + }); +} From 17308f5d17d3706e71d2b269bdc2ddfef3d09172 Mon Sep 17 00:00:00 2001 From: Cameron Fairchild <cameron@opentensor.ai> Date: Tue, 28 Jan 2025 13:40:02 -0500 Subject: [PATCH 108/145] disable TAO into subnet if reg are disabled --- .../subtensor/src/coinbase/run_coinbase.rs | 34 +++++++++++-------- 1 file changed, 20 insertions(+), 14 deletions(-) diff --git a/pallets/subtensor/src/coinbase/run_coinbase.rs b/pallets/subtensor/src/coinbase/run_coinbase.rs index 7516857b4..b57a08503 100644 --- a/pallets/subtensor/src/coinbase/run_coinbase.rs +++ b/pallets/subtensor/src/coinbase/run_coinbase.rs @@ -122,20 +122,26 @@ impl<T: Config> Pallet<T> { netuid, subnet_proportion ); - // 3.7: Calculate subnet's TAO emission: E_s = P_s * E_m - let tao_in: u64 = mech_emission - .checked_mul(subnet_proportion) - .unwrap_or(I96F32::saturating_from_num(0)) - .saturating_to_num::<u64>(); - log::debug!( - "Subnet TAO emission (E_s) for netuid {:?}: {:?}", - netuid, - tao_in - ); - // 3.8: Store the subnet TAO emission. - *tao_in_map.entry(*netuid).or_insert(0) = tao_in; - // 3.9: Store the block emission for this subnet for chain storage. - EmissionValues::<T>::insert(*netuid, tao_in); + + // Only emit TAO if the subnetwork allows registration. + if Self::get_network_registration_allowed(*netuid) + || Self::get_network_pow_registration_allowed(*netuid) + { + // 3.7: Calculate subnet's TAO emission: E_s = P_s * E_m + let tao_in: u64 = mech_emission + .checked_mul(subnet_proportion) + .unwrap_or(I96F32::saturating_from_num(0)) + .saturating_to_num::<u64>(); + log::debug!( + "Subnet TAO emission (E_s) for netuid {:?}: {:?}", + netuid, + tao_in + ); + // 3.8: Store the subnet TAO emission. + *tao_in_map.entry(*netuid).or_insert(0) = tao_in; + // 3.9: Store the block emission for this subnet for chain storage. + EmissionValues::<T>::insert(*netuid, tao_in); + } } // == We'll save the owner cuts for each subnet. From e0e2b84c04dc72aa1d8999c54d1d7c0097979f9b Mon Sep 17 00:00:00 2001 From: Cameron Fairchild <cameron@opentensor.ai> Date: Tue, 28 Jan 2025 13:49:20 -0500 Subject: [PATCH 109/145] add stake info by hk, ck, netuid --- pallets/subtensor/runtime-api/src/lib.rs | 1 + pallets/subtensor/src/rpc_info/stake_info.rs | 38 ++++++++++++++++++++ runtime/src/lib.rs | 5 +++ 3 files changed, 44 insertions(+) diff --git a/pallets/subtensor/runtime-api/src/lib.rs b/pallets/subtensor/runtime-api/src/lib.rs index cdcd6ed39..31f351aeb 100644 --- a/pallets/subtensor/runtime-api/src/lib.rs +++ b/pallets/subtensor/runtime-api/src/lib.rs @@ -34,6 +34,7 @@ sp_api::decl_runtime_apis! { pub trait StakeInfoRuntimeApi { fn get_stake_info_for_coldkey( coldkey_account_vec: Vec<u8> ) -> Vec<u8>; fn get_stake_info_for_coldkeys( coldkey_account_vecs: Vec<Vec<u8>> ) -> Vec<u8>; + fn get_stake_info_for_hotkey_coldkey_netuid( hotkey_account_vec: Vec<u8>, coldkey_account_vec: Vec<u8>, netuid: u16 ) -> Vec<u8>; } pub trait SubnetRegistrationRuntimeApi { diff --git a/pallets/subtensor/src/rpc_info/stake_info.rs b/pallets/subtensor/src/rpc_info/stake_info.rs index 97afd5aa2..0422bc33b 100644 --- a/pallets/subtensor/src/rpc_info/stake_info.rs +++ b/pallets/subtensor/src/rpc_info/stake_info.rs @@ -99,4 +99,42 @@ impl<T: Config> Pallet<T> { first.1.clone() } } + + pub fn get_stake_info_for_hotkey_coldkey_netuid( + hotkey_account_vec: Vec<u8>, + coldkey_account_vec: Vec<u8>, + netuid: u16, + ) -> Option<StakeInfo<T>> { + if coldkey_account_vec.len() != 32 { + return None; // Invalid coldkey + } + + let Ok(coldkey) = T::AccountId::decode(&mut coldkey_account_vec.as_bytes_ref()) else { + return None; + }; + + if hotkey_account_vec.len() != 32 { + return None; // Invalid hotkey + } + + let Ok(hotkey) = T::AccountId::decode(&mut hotkey_account_vec.as_bytes_ref()) else { + return None; + }; + + let alpha: u64 = + Self::get_stake_for_hotkey_and_coldkey_on_subnet(&hotkey, &coldkey, netuid); + let emission: u64 = AlphaDividendsPerSubnet::<T>::get(netuid, &hotkey); + let is_registered: bool = Self::is_hotkey_registered_on_network(netuid, &hotkey); + + Some(StakeInfo { + hotkey: hotkey.clone(), + coldkey: coldkey.clone(), + netuid: (netuid).into(), + stake: alpha.into(), + locked: 0.into(), + emission: emission.into(), + drain: 0.into(), + is_registered, + }) + } } diff --git a/runtime/src/lib.rs b/runtime/src/lib.rs index 372c9a981..93357f227 100644 --- a/runtime/src/lib.rs +++ b/runtime/src/lib.rs @@ -2157,6 +2157,11 @@ impl_runtime_apis! { let result = SubtensorModule::get_stake_info_for_coldkeys( coldkey_account_vecs ); result.encode() } + + fn get_stake_for_hotkey_coldkey_netuid( hotkey_account_vec: Vec<u8>, coldkey_account_vec: Vec<u8>, netuid: u16 ) -> Vec<u8> { + let result = SubtensorModule::get_stake_info_for_hotkey_coldkey_netuid( hotkey_account_vec, coldkey_account_vec, netuid ); + result.encode() + } } impl subtensor_custom_rpc_runtime_api::SubnetRegistrationRuntimeApi<Block> for Runtime { From 067ddd3b328cc15f005ba8f4d0d55e287bdfc3f1 Mon Sep 17 00:00:00 2001 From: Cameron Fairchild <cameron@opentensor.ai> Date: Tue, 28 Jan 2025 13:50:09 -0500 Subject: [PATCH 110/145] Revert "add stake info by hk, ck, netuid" This reverts commit e0e2b84c04dc72aa1d8999c54d1d7c0097979f9b. --- pallets/subtensor/runtime-api/src/lib.rs | 1 - pallets/subtensor/src/rpc_info/stake_info.rs | 38 -------------------- runtime/src/lib.rs | 5 --- 3 files changed, 44 deletions(-) diff --git a/pallets/subtensor/runtime-api/src/lib.rs b/pallets/subtensor/runtime-api/src/lib.rs index 31f351aeb..cdcd6ed39 100644 --- a/pallets/subtensor/runtime-api/src/lib.rs +++ b/pallets/subtensor/runtime-api/src/lib.rs @@ -34,7 +34,6 @@ sp_api::decl_runtime_apis! { pub trait StakeInfoRuntimeApi { fn get_stake_info_for_coldkey( coldkey_account_vec: Vec<u8> ) -> Vec<u8>; fn get_stake_info_for_coldkeys( coldkey_account_vecs: Vec<Vec<u8>> ) -> Vec<u8>; - fn get_stake_info_for_hotkey_coldkey_netuid( hotkey_account_vec: Vec<u8>, coldkey_account_vec: Vec<u8>, netuid: u16 ) -> Vec<u8>; } pub trait SubnetRegistrationRuntimeApi { diff --git a/pallets/subtensor/src/rpc_info/stake_info.rs b/pallets/subtensor/src/rpc_info/stake_info.rs index 0422bc33b..97afd5aa2 100644 --- a/pallets/subtensor/src/rpc_info/stake_info.rs +++ b/pallets/subtensor/src/rpc_info/stake_info.rs @@ -99,42 +99,4 @@ impl<T: Config> Pallet<T> { first.1.clone() } } - - pub fn get_stake_info_for_hotkey_coldkey_netuid( - hotkey_account_vec: Vec<u8>, - coldkey_account_vec: Vec<u8>, - netuid: u16, - ) -> Option<StakeInfo<T>> { - if coldkey_account_vec.len() != 32 { - return None; // Invalid coldkey - } - - let Ok(coldkey) = T::AccountId::decode(&mut coldkey_account_vec.as_bytes_ref()) else { - return None; - }; - - if hotkey_account_vec.len() != 32 { - return None; // Invalid hotkey - } - - let Ok(hotkey) = T::AccountId::decode(&mut hotkey_account_vec.as_bytes_ref()) else { - return None; - }; - - let alpha: u64 = - Self::get_stake_for_hotkey_and_coldkey_on_subnet(&hotkey, &coldkey, netuid); - let emission: u64 = AlphaDividendsPerSubnet::<T>::get(netuid, &hotkey); - let is_registered: bool = Self::is_hotkey_registered_on_network(netuid, &hotkey); - - Some(StakeInfo { - hotkey: hotkey.clone(), - coldkey: coldkey.clone(), - netuid: (netuid).into(), - stake: alpha.into(), - locked: 0.into(), - emission: emission.into(), - drain: 0.into(), - is_registered, - }) - } } diff --git a/runtime/src/lib.rs b/runtime/src/lib.rs index 93357f227..372c9a981 100644 --- a/runtime/src/lib.rs +++ b/runtime/src/lib.rs @@ -2157,11 +2157,6 @@ impl_runtime_apis! { let result = SubtensorModule::get_stake_info_for_coldkeys( coldkey_account_vecs ); result.encode() } - - fn get_stake_for_hotkey_coldkey_netuid( hotkey_account_vec: Vec<u8>, coldkey_account_vec: Vec<u8>, netuid: u16 ) -> Vec<u8> { - let result = SubtensorModule::get_stake_info_for_hotkey_coldkey_netuid( hotkey_account_vec, coldkey_account_vec, netuid ); - result.encode() - } } impl subtensor_custom_rpc_runtime_api::SubnetRegistrationRuntimeApi<Block> for Runtime { From 19d35a6f0baa8e3824e29979ef9ce5a81ce74eb8 Mon Sep 17 00:00:00 2001 From: Cameron Fairchild <cameron@opentensor.ai> Date: Tue, 28 Jan 2025 13:51:13 -0500 Subject: [PATCH 111/145] Add stakeinfo by hk, ck, netuid --- pallets/subtensor/runtime-api/src/lib.rs | 1 + pallets/subtensor/src/rpc_info/stake_info.rs | 38 ++++++++++++++++++++ runtime/src/lib.rs | 5 +++ 3 files changed, 44 insertions(+) diff --git a/pallets/subtensor/runtime-api/src/lib.rs b/pallets/subtensor/runtime-api/src/lib.rs index cdcd6ed39..31f351aeb 100644 --- a/pallets/subtensor/runtime-api/src/lib.rs +++ b/pallets/subtensor/runtime-api/src/lib.rs @@ -34,6 +34,7 @@ sp_api::decl_runtime_apis! { pub trait StakeInfoRuntimeApi { fn get_stake_info_for_coldkey( coldkey_account_vec: Vec<u8> ) -> Vec<u8>; fn get_stake_info_for_coldkeys( coldkey_account_vecs: Vec<Vec<u8>> ) -> Vec<u8>; + fn get_stake_info_for_hotkey_coldkey_netuid( hotkey_account_vec: Vec<u8>, coldkey_account_vec: Vec<u8>, netuid: u16 ) -> Vec<u8>; } pub trait SubnetRegistrationRuntimeApi { diff --git a/pallets/subtensor/src/rpc_info/stake_info.rs b/pallets/subtensor/src/rpc_info/stake_info.rs index 97afd5aa2..0422bc33b 100644 --- a/pallets/subtensor/src/rpc_info/stake_info.rs +++ b/pallets/subtensor/src/rpc_info/stake_info.rs @@ -99,4 +99,42 @@ impl<T: Config> Pallet<T> { first.1.clone() } } + + pub fn get_stake_info_for_hotkey_coldkey_netuid( + hotkey_account_vec: Vec<u8>, + coldkey_account_vec: Vec<u8>, + netuid: u16, + ) -> Option<StakeInfo<T>> { + if coldkey_account_vec.len() != 32 { + return None; // Invalid coldkey + } + + let Ok(coldkey) = T::AccountId::decode(&mut coldkey_account_vec.as_bytes_ref()) else { + return None; + }; + + if hotkey_account_vec.len() != 32 { + return None; // Invalid hotkey + } + + let Ok(hotkey) = T::AccountId::decode(&mut hotkey_account_vec.as_bytes_ref()) else { + return None; + }; + + let alpha: u64 = + Self::get_stake_for_hotkey_and_coldkey_on_subnet(&hotkey, &coldkey, netuid); + let emission: u64 = AlphaDividendsPerSubnet::<T>::get(netuid, &hotkey); + let is_registered: bool = Self::is_hotkey_registered_on_network(netuid, &hotkey); + + Some(StakeInfo { + hotkey: hotkey.clone(), + coldkey: coldkey.clone(), + netuid: (netuid).into(), + stake: alpha.into(), + locked: 0.into(), + emission: emission.into(), + drain: 0.into(), + is_registered, + }) + } } diff --git a/runtime/src/lib.rs b/runtime/src/lib.rs index 372c9a981..93357f227 100644 --- a/runtime/src/lib.rs +++ b/runtime/src/lib.rs @@ -2157,6 +2157,11 @@ impl_runtime_apis! { let result = SubtensorModule::get_stake_info_for_coldkeys( coldkey_account_vecs ); result.encode() } + + fn get_stake_for_hotkey_coldkey_netuid( hotkey_account_vec: Vec<u8>, coldkey_account_vec: Vec<u8>, netuid: u16 ) -> Vec<u8> { + let result = SubtensorModule::get_stake_info_for_hotkey_coldkey_netuid( hotkey_account_vec, coldkey_account_vec, netuid ); + result.encode() + } } impl subtensor_custom_rpc_runtime_api::SubnetRegistrationRuntimeApi<Block> for Runtime { From fce08351e36232b994bbdc570498398cbe875209 Mon Sep 17 00:00:00 2001 From: Cameron Fairchild <cameron@opentensor.ai> Date: Tue, 28 Jan 2025 13:54:07 -0500 Subject: [PATCH 112/145] typo --- runtime/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/runtime/src/lib.rs b/runtime/src/lib.rs index 93357f227..a2cb120e1 100644 --- a/runtime/src/lib.rs +++ b/runtime/src/lib.rs @@ -2158,7 +2158,7 @@ impl_runtime_apis! { result.encode() } - fn get_stake_for_hotkey_coldkey_netuid( hotkey_account_vec: Vec<u8>, coldkey_account_vec: Vec<u8>, netuid: u16 ) -> Vec<u8> { + fn get_stake_info_for_hotkey_coldkey_netuid( hotkey_account_vec: Vec<u8>, coldkey_account_vec: Vec<u8>, netuid: u16 ) -> Vec<u8> { let result = SubtensorModule::get_stake_info_for_hotkey_coldkey_netuid( hotkey_account_vec, coldkey_account_vec, netuid ); result.encode() } From 5a1f1f23c99c9898745b52c426aed31d90b51b94 Mon Sep 17 00:00:00 2001 From: Aliaksandr Tsurko <ales@opentensor.dev> Date: Tue, 28 Jan 2025 19:11:27 +0100 Subject: [PATCH 113/145] Replace frontier's RuntimeHelper with manual dispatcher --- Cargo.lock | 45 ----------- runtime/Cargo.toml | 16 ++-- runtime/src/precompiles/balance_transfer.rs | 32 +++----- runtime/src/precompiles/mod.rs | 82 ++++++++++++++++++++- runtime/src/precompiles/staking.rs | 26 +------ 5 files changed, 97 insertions(+), 104 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index e86ef3d69..964c583e1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1067,12 +1067,6 @@ dependencies = [ "thiserror", ] -[[package]] -name = "case" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd6c0e7b807d60291f42f33f58480c0bfafe28ed08286446f45e463728cf9c1c" - [[package]] name = "cc" version = "1.1.24" @@ -5664,7 +5658,6 @@ dependencies = [ "pallet-transaction-payment-rpc-runtime-api", "pallet-utility", "parity-scale-codec", - "precompile-utils", "rand_chacha", "scale-info", "serde_json", @@ -7017,44 +7010,6 @@ dependencies = [ "zerocopy", ] -[[package]] -name = "precompile-utils" -version = "0.1.0" -source = "git+https://github.com/opentensor/frontier?rev=635bdac882#635bdac882333afed827053f31ef56ab739f7a2e" -dependencies = [ - "environmental", - "evm", - "fp-evm", - "frame-support", - "frame-system", - "hex", - "impl-trait-for-tuples", - "log", - "num_enum", - "pallet-evm", - "parity-scale-codec", - "precompile-utils-macro", - "sp-core", - "sp-io", - "sp-runtime", - "sp-weights", - "staging-xcm", -] - -[[package]] -name = "precompile-utils-macro" -version = "0.1.0" -source = "git+https://github.com/opentensor/frontier?rev=635bdac882#635bdac882333afed827053f31ef56ab739f7a2e" -dependencies = [ - "case", - "num_enum", - "prettyplease 0.2.22", - "proc-macro2", - "quote", - "sp-crypto-hashing 0.1.0 (git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-stable2409)", - "syn 1.0.109", -] - [[package]] name = "predicates" version = "2.1.5" diff --git a/runtime/Cargo.toml b/runtime/Cargo.toml index 0d44e0907..171ef3c47 100644 --- a/runtime/Cargo.toml +++ b/runtime/Cargo.toml @@ -20,7 +20,9 @@ name = "spec_version" path = "src/spec_version.rs" [dependencies] -ed25519-dalek = { workspace = true, default-features = false, features = ["alloc"] } +ed25519-dalek = { workspace = true, default-features = false, features = [ + "alloc", +] } subtensor-macros.workspace = true subtensor-custom-rpc-runtime-api = { path = "../pallets/subtensor/runtime-api", default-features = false } smallvec = { workspace = true } @@ -98,7 +100,6 @@ pallet-commitments = { default-features = false, path = "../pallets/commitments" fp-evm = { workspace = true } fp-rpc = { workspace = true } fp-self-contained = { workspace = true } -precompile-utils = { workspace = true } # Frontier FRAME pallet-base-fee = { workspace = true } @@ -133,9 +134,7 @@ substrate-wasm-builder = { workspace = true, optional = true } [features] default = ["std"] pow-faucet = ["pallet-subtensor/pow-faucet"] -fast-blocks = [ - "pallet-subtensor/fast-blocks" -] +fast-blocks = ["pallet-subtensor/fast-blocks"] std = [ "frame-try-runtime?/std", "frame-system-benchmarking?/std", @@ -192,7 +191,6 @@ std = [ "fp-evm/std", "fp-rpc/std", "fp-self-contained/std", - "precompile-utils/std", # Frontier FRAME "pallet-base-fee/std", "pallet-dynamic-fee/std", @@ -211,7 +209,7 @@ std = [ "hex/std", "rand_chacha/std", "sha2/std", - "w3f-bls/std" + "w3f-bls/std", ] runtime-benchmarks = [ "frame-benchmarking/runtime-benchmarks", @@ -240,7 +238,7 @@ runtime-benchmarks = [ "pallet-ethereum/runtime-benchmarks", "pallet-evm/runtime-benchmarks", "pallet-hotfix-sufficients/runtime-benchmarks", - "pallet-drand/runtime-benchmarks" + "pallet-drand/runtime-benchmarks", ] try-runtime = [ "frame-try-runtime/try-runtime", @@ -276,6 +274,6 @@ try-runtime = [ "pallet-ethereum/try-runtime", "pallet-evm/try-runtime", "pallet-evm-chain-id/try-runtime", - "pallet-drand/try-runtime" + "pallet-drand/try-runtime", ] metadata-hash = ["substrate-wasm-builder/metadata-hash"] diff --git a/runtime/src/precompiles/balance_transfer.rs b/runtime/src/precompiles/balance_transfer.rs index 03c4be8a6..177ced50e 100644 --- a/runtime/src/precompiles/balance_transfer.rs +++ b/runtime/src/precompiles/balance_transfer.rs @@ -1,15 +1,14 @@ use frame_system::RawOrigin; use pallet_evm::{ - BalanceConverter, ExitError, ExitSucceed, PrecompileFailure, PrecompileHandle, - PrecompileOutput, PrecompileResult, + BalanceConverter, ExitError, ExitSucceed, PrecompileHandle, PrecompileOutput, PrecompileResult, }; -use precompile_utils::prelude::RuntimeHelper; -use sp_core::U256; use sp_runtime::traits::UniqueSaturatedInto; use sp_std::vec; -use crate::precompiles::{bytes_to_account_id, get_method_id, get_slice}; -use crate::{Runtime, RuntimeCall}; +use crate::precompiles::{ + bytes_to_account_id, get_method_id, get_slice, try_dispatch_runtime_call, +}; +use crate::Runtime; pub const BALANCE_TRANSFER_INDEX: u64 = 2048; @@ -37,7 +36,7 @@ impl BalanceTransferPrecompile { } // Forward all received value to the destination address - let amount: U256 = handle.context().apparent_value; + let amount = handle.context().apparent_value; // Use BalanceConverter to convert EVM amount to Substrate balance let amount_sub = @@ -55,23 +54,12 @@ impl BalanceTransferPrecompile { let account_id_src = bytes_to_account_id(&CONTRACT_ADDRESS_SS58)?; let account_id_dst = bytes_to_account_id(address_bytes_dst)?; - let call = RuntimeCall::Balances(pallet_balances::Call::<Runtime>::transfer_allow_death { + let call = pallet_balances::Call::<Runtime>::transfer_allow_death { dest: account_id_dst.into(), value: amount_sub.unique_saturated_into(), - }); + }; + let origin = RawOrigin::Signed(account_id_src); - // Dispatch the call - RuntimeHelper::<Runtime>::try_dispatch( - handle, - RawOrigin::Signed(account_id_src).into(), - call, - ) - .map(|_| PrecompileOutput { - exit_status: ExitSucceed::Returned, - output: vec![], - }) - .map_err(|_| PrecompileFailure::Error { - exit_status: ExitError::OutOfFund, - }) + try_dispatch_runtime_call(handle, call, origin) } } diff --git a/runtime/src/precompiles/mod.rs b/runtime/src/precompiles/mod.rs index e13516e95..e0cb85f3b 100644 --- a/runtime/src/precompiles/mod.rs +++ b/runtime/src/precompiles/mod.rs @@ -1,14 +1,21 @@ +extern crate alloc; + +use alloc::format; use core::marker::PhantomData; -use sp_core::{hashing::keccak_256, H160}; -use sp_runtime::AccountId32; +use frame_support::dispatch::{GetDispatchInfo, Pays}; +use frame_system::RawOrigin; use pallet_evm::{ - ExitError, IsPrecompileResult, Precompile, PrecompileFailure, PrecompileHandle, - PrecompileResult, PrecompileSet, + ExitError, ExitSucceed, GasWeightMapping, IsPrecompileResult, Precompile, PrecompileFailure, + PrecompileHandle, PrecompileOutput, PrecompileResult, PrecompileSet, }; use pallet_evm_precompile_modexp::Modexp; use pallet_evm_precompile_sha3fips::Sha3FIPS256; use pallet_evm_precompile_simple::{ECRecover, ECRecoverPublicKey, Identity, Ripemd160, Sha256}; +use sp_core::{hashing::keccak_256, H160}; +use sp_runtime::{traits::Dispatchable, AccountId32}; + +use crate::{Runtime, RuntimeCall}; // Include custom precompiles mod balance_transfer; @@ -130,3 +137,70 @@ pub fn get_slice(data: &[u8], from: usize, to: usize) -> Result<&[u8], Precompil }) } } + +/// Dispatches a runtime call, but also checks and records the gas costs. +fn try_dispatch_runtime_call( + handle: &mut impl PrecompileHandle, + call: impl Into<RuntimeCall>, + origin: RawOrigin<AccountId32>, +) -> PrecompileResult { + let call = Into::<RuntimeCall>::into(call); + let info = call.get_dispatch_info(); + + let target_gas = handle.gas_limit(); + if let Some(gas) = target_gas { + let valid_weight = + <Runtime as pallet_evm::Config>::GasWeightMapping::gas_to_weight(gas, false).ref_time(); + if info.weight.ref_time() > valid_weight { + return Err(PrecompileFailure::Error { + exit_status: ExitError::OutOfGas, + }); + } + } + + handle.record_external_cost( + Some(info.weight.ref_time()), + Some(info.weight.proof_size()), + None, + )?; + + match call.dispatch(origin.into()) { + Ok(post_info) => { + if post_info.pays_fee(&info) == Pays::Yes { + let actual_weight = post_info.actual_weight.unwrap_or(info.weight); + let cost = + <Runtime as pallet_evm::Config>::GasWeightMapping::weight_to_gas(actual_weight); + handle.record_cost(cost)?; + + handle.refund_external_cost( + Some( + info.weight + .ref_time() + .saturating_sub(actual_weight.ref_time()), + ), + Some( + info.weight + .proof_size() + .saturating_sub(actual_weight.proof_size()), + ), + ); + } + + log::info!("Dispatch succeeded. Post info: {:?}", post_info); + + Ok(PrecompileOutput { + exit_status: ExitSucceed::Returned, + output: Default::default(), + }) + } + Err(e) => { + log::error!("Dispatch failed. Error: {:?}", e); + log::warn!("Returning error PrecompileFailure::Error"); + Err(PrecompileFailure::Error { + exit_status: ExitError::Other( + format!("dispatch execution failed: {}", <&'static str>::from(e)).into(), + ), + }) + } + } +} diff --git a/runtime/src/precompiles/staking.rs b/runtime/src/precompiles/staking.rs index f8534927b..57e706026 100644 --- a/runtime/src/precompiles/staking.rs +++ b/runtime/src/precompiles/staking.rs @@ -30,7 +30,6 @@ use pallet_evm::{ AddressMapping, BalanceConverter, ExitError, ExitSucceed, HashedAddressMapping, PrecompileFailure, PrecompileHandle, PrecompileOutput, PrecompileResult, }; -use precompile_utils::prelude::RuntimeHelper; use sp_core::crypto::Ss58Codec; use sp_core::U256; use sp_runtime::traits::{BlakeTwo256, Dispatchable, StaticLookup, UniqueSaturatedInto}; @@ -38,7 +37,7 @@ use sp_runtime::AccountId32; use sp_std::vec; use crate::{ - precompiles::{get_method_id, get_slice}, + precompiles::{get_method_id, get_slice, try_dispatch_runtime_call}, ProxyType, Runtime, RuntimeCall, }; @@ -214,28 +213,7 @@ impl StakingPrecompile { Self::transfer_back_to_caller(&account_id, amount)?; } - match RuntimeHelper::<Runtime>::try_dispatch( - handle, - RawOrigin::Signed(account_id.clone()).into(), - call, - ) { - Ok(post_info) => { - log::info!("Dispatch succeeded. Post info: {:?}", post_info); - - Ok(PrecompileOutput { - exit_status: ExitSucceed::Returned, - output: vec![], - }) - } - - Err(dispatch_error) => { - log::error!("Dispatch failed. Error: {:?}", dispatch_error); - log::warn!("Returning error PrecompileFailure::Error"); - Err(PrecompileFailure::Error { - exit_status: ExitError::Other("Subtensor call failed".into()), - }) - } - } + try_dispatch_runtime_call(handle, call, RawOrigin::Signed(account_id.clone())) } fn transfer_back_to_caller( From 2bf58540b3de575bf5dc5e4bd3d9b3c877103c60 Mon Sep 17 00:00:00 2001 From: Cameron Fairchild <cameron@opentensor.ai> Date: Tue, 28 Jan 2025 13:58:33 -0500 Subject: [PATCH 114/145] add subnet volume to runtimeAPIs --- pallets/subtensor/src/rpc_info/dynamic_info.rs | 4 +++- pallets/subtensor/src/rpc_info/metagraph.rs | 6 +++++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/pallets/subtensor/src/rpc_info/dynamic_info.rs b/pallets/subtensor/src/rpc_info/dynamic_info.rs index a8ffd8549..bbee27c55 100644 --- a/pallets/subtensor/src/rpc_info/dynamic_info.rs +++ b/pallets/subtensor/src/rpc_info/dynamic_info.rs @@ -4,7 +4,7 @@ use codec::Compact; use frame_support::pallet_prelude::{Decode, Encode}; use subtensor_macros::freeze_struct; -#[freeze_struct("44fd17b240416875")] +#[freeze_struct("1be5a1e26a82082f")] #[derive(Decode, Encode, PartialEq, Eq, Clone, Debug)] pub struct DynamicInfo<T: Config> { netuid: Compact<u16>, @@ -24,6 +24,7 @@ pub struct DynamicInfo<T: Config> { tao_in_emission: Compact<u64>, pending_alpha_emission: Compact<u64>, pending_root_emission: Compact<u64>, + subnet_volume: Compact<u64>, network_registered_at: Compact<u64>, subnet_identity: Option<SubnetIdentity>, } @@ -60,6 +61,7 @@ impl<T: Config> Pallet<T> { tao_in_emission: SubnetTaoInEmission::<T>::get(netuid).into(), pending_alpha_emission: PendingEmission::<T>::get(netuid).into(), pending_root_emission: PendingRootDivs::<T>::get(netuid).into(), + subnet_volume: SubnetVolume::<T>::get(netuid).into(), network_registered_at: NetworkRegisteredAt::<T>::get(netuid).into(), subnet_identity: SubnetIdentities::<T>::get(netuid), }) diff --git a/pallets/subtensor/src/rpc_info/metagraph.rs b/pallets/subtensor/src/rpc_info/metagraph.rs index 1e77e5fe6..ee3864932 100644 --- a/pallets/subtensor/src/rpc_info/metagraph.rs +++ b/pallets/subtensor/src/rpc_info/metagraph.rs @@ -6,7 +6,7 @@ use frame_support::pallet_prelude::{Decode, Encode}; use substrate_fixed::types::I64F64; use subtensor_macros::freeze_struct; -#[freeze_struct("bce2310daa502e48")] +#[freeze_struct("fa24d156067e5eb9")] #[derive(Decode, Encode, PartialEq, Eq, Clone, Debug)] pub struct Metagraph<T: Config> { // Subnet index @@ -38,6 +38,7 @@ pub struct Metagraph<T: Config> { tao_in_emission: Compact<u64>, // amount of tao injected per block pending_alpha_emission: Compact<u64>, // pending alpha to be distributed pending_root_emission: Compact<u64>, // panding tao for root divs to be distributed + subnet_volume: Compact<u64>, // volume of the subnet in TAO // Hparams for epoch rho: Compact<u16>, // subnet rho param @@ -141,6 +142,8 @@ impl<T: Config> Pallet<T> { Vec<I64F64>, Vec<I64F64>, ) = Self::get_stake_weights_for_network(netuid); + + let subnet_volume = SubnetVolume::<T>::get(netuid); Some(Metagraph { // Subnet index netuid: netuid.into(), // subnet index. @@ -177,6 +180,7 @@ impl<T: Config> Pallet<T> { tao_in_emission: SubnetTaoInEmission::<T>::get(netuid).into(), // amount of tao injected per block pending_alpha_emission: PendingEmission::<T>::get(netuid).into(), // pending alpha to be distributed pending_root_emission: PendingRootDivs::<T>::get(netuid).into(), // panding tao for root divs to be distributed + subnet_volume: subnet_volume.into(), // Hparams for epoch rho: Self::get_rho(netuid).into(), // subnet rho param From d66f77f1ceabe392459881d8db37a3e03441404e Mon Sep 17 00:00:00 2001 From: Greg Zaitsev <gregz@opentensor.dev> Date: Tue, 28 Jan 2025 14:58:45 -0500 Subject: [PATCH 115/145] Move checked_sqrt to save math and add tests --- pallets/subtensor/src/epoch/math.rs | 48 ------- pallets/subtensor/src/staking/add_stake.rs | 16 +-- primitives/safe-math/src/lib.rs | 153 +++++++++++++++++---- 3 files changed, 136 insertions(+), 81 deletions(-) diff --git a/pallets/subtensor/src/epoch/math.rs b/pallets/subtensor/src/epoch/math.rs index c0c9616c6..faa7ce21e 100644 --- a/pallets/subtensor/src/epoch/math.rs +++ b/pallets/subtensor/src/epoch/math.rs @@ -1428,51 +1428,3 @@ pub fn safe_ln(value: I32F32) -> I32F32 { pub fn safe_exp(value: I32F32) -> I32F32 { exp(value).unwrap_or(I32F32::saturating_from_num(0.0)) } - -fn abs_diff(a: U96F32, b: U96F32) -> U96F32 { - if a < b { - b.saturating_sub(a) - } else { - a.saturating_sub(b) - } -} - -/// Safe sqrt with good precision -pub fn checked_sqrt(value: U96F32, epsilon: U96F32) -> Option<U96F32> { - let zero: U96F32 = U96F32::from_num(0); - let two: U96F32 = U96F32::from_num(2); - - // print!("sqrt({:?}) = ...", value); - - if value < zero { - return None; - } - - let mut high: U96F32 = value; - let mut low: U96F32 = zero; - let mut middle: U96F32 = (high + low) / two; - - let mut iteration = 0; - let max_iterations = 128; - - // Iterative approximation using bisection - while abs_diff(value.checked_div(middle).unwrap_or(zero), middle) > epsilon { - // println!("abs diff = {:?}", abs_diff(value.checked_div(middle).unwrap_or(zero), middle)); - - if value.checked_div(middle).unwrap_or(zero) < middle { - high = middle; - } else { - low = middle; - } - - middle = (high + low) / two; - - iteration += 1; - if iteration > max_iterations { - break; - } - } - - // println!("iterations = {:?}, result = {:?}", iteration, middle); - Some(middle) -} diff --git a/pallets/subtensor/src/staking/add_stake.rs b/pallets/subtensor/src/staking/add_stake.rs index 1dc75664f..793488824 100644 --- a/pallets/subtensor/src/staking/add_stake.rs +++ b/pallets/subtensor/src/staking/add_stake.rs @@ -155,19 +155,19 @@ impl<T: Config> Pallet<T> { if alpha_in == 0 { return 0; } - let alpha_in_float: U96F32 = U96F32::from_num(alpha_in); + let alpha_in_float: U96F32 = U96F32::saturating_from_num(alpha_in); // Corner case: SubnetTAO is zero. Staking can't happen, so max amount is zero. let tao_reserve = SubnetTAO::<T>::get(netuid); if tao_reserve == 0 { return 0; } - let tao_reserve_float: U96F32 = U96F32::from_num(tao_reserve); + let tao_reserve_float: U96F32 = U96F32::saturating_from_num(tao_reserve); // Corner case: limit_price < current_price (price cannot decrease with staking) - let limit_price_float: U96F32 = U96F32::from_num(limit_price) - .checked_div(U96F32::from_num(1_000_000_000)) - .unwrap_or(U96F32::from_num(0)); + let limit_price_float: U96F32 = U96F32::saturating_from_num(limit_price) + .checked_div(U96F32::saturating_from_num(1_000_000_000)) + .unwrap_or(U96F32::saturating_from_num(0)); if limit_price_float < Self::get_alpha_price(netuid) { return 0; } @@ -175,16 +175,16 @@ impl<T: Config> Pallet<T> { // Main case: return SQRT(limit_price * SubnetTAO * SubnetAlphaIn) - SubnetTAO // This is the positive solution of quare equation for finding additional TAO from // limit_price. - let zero: U96F32 = U96F32::from_num(0.0); + let zero: U96F32 = U96F32::saturating_from_num(0.0); let sqrt: U96F32 = checked_sqrt( limit_price_float .saturating_mul(tao_reserve_float) .saturating_mul(alpha_in_float), - U96F32::from_num(0.1), + U96F32::saturating_from_num(0.1), ) .unwrap_or(zero); - U96F32::from_num(sqrt) + U96F32::saturating_from_num(sqrt) .saturating_sub(U96F32::saturating_from_num(tao_reserve_float)) .saturating_to_num::<u64>() } diff --git a/primitives/safe-math/src/lib.rs b/primitives/safe-math/src/lib.rs index 83c27b07c..e642793f8 100644 --- a/primitives/safe-math/src/lib.rs +++ b/primitives/safe-math/src/lib.rs @@ -1,7 +1,7 @@ #![cfg_attr(not(feature = "std"), no_std)] #![allow(clippy::result_unit_err)] -use substrate_fixed::types::{I110F18, I32F32, I64F64, I96F32, U64F64}; +use substrate_fixed::types::{I110F18, I32F32, I64F64, I96F32, U64F64, U110F18}; /// Safe division trait pub trait SafeDiv { @@ -45,27 +45,130 @@ macro_rules! impl_safe_div_for_fixed { )* }; } -impl_safe_div_for_fixed!(I96F32, I32F32, I64F64, I110F18, U64F64); - -// /// Trait for safe conversion to primitive type P -// pub trait SafeToNum<T> { -// /// Safe conversion to primitive type P -// fn safe_to_num<P>(self) -> P -// where -// P: num_traits::Bounded + substrate_fixed::prelude::ToFixed + substrate_fixed::prelude::FromFixed; -// } - -// impl<T> SafeToNum<T> for T -// where -// T: substrate_fixed::traits::Fixed, -// { -// fn safe_to_num<P>(self) -> P -// where -// P: num_traits::Bounded + substrate_fixed::prelude::ToFixed + substrate_fixed::prelude::FromFixed -// { -// match self.try_into() { -// Ok(value) => value, -// Err(_) => P::max_value(), -// } -// } -// } +impl_safe_div_for_fixed!(I96F32, I32F32, I64F64, I110F18, U64F64, U110F18); + +fn abs_diff(a: U110F18, b: U110F18) -> U110F18 { + if a < b { + b.saturating_sub(a) + } else { + a.saturating_sub(b) + } +} + +/// Safe sqrt with good precision +pub fn checked_sqrt(value: U110F18, epsilon: U110F18) -> Option<U110F18> { + let zero: U110F18 = U110F18::from_num(0); + let two: U110F18 = U110F18::from_num(2); + + if value < zero { + return None; + } + + let mut high: U110F18 = value; + let mut low: U110F18 = zero; + let mut middle: U110F18 = (high + low) / two; + + let mut iteration = 0; + let max_iterations = 128; + let mut check_val: U110F18 = value.safe_div(middle); + + // Iterative approximation using bisection + while abs_diff(check_val, middle) > epsilon { + if check_val < middle { + high = middle; + } else { + low = middle; + } + + middle = (high + low) / two; + check_val = value.safe_div(middle); + + iteration += 1; + if iteration > max_iterations { + break; + } + } + + Some(middle) +} + +#[cfg(test)] +mod tests { + use super::*; + use substrate_fixed::types::U110F18; // Assuming U110F18 is properly imported + + // Helper function for absolute difference + fn abs_diff(a: U110F18, b: U110F18) -> U110F18 { + if a > b { + a - b + } else { + b - a + } + } + + #[test] + fn test_checked_sqrt_positive_values() { + let value: U110F18 = U110F18::from_num(4.0); + let epsilon: U110F18 = U110F18::from_num(0.0001); + + let result: Option<U110F18> = checked_sqrt(value, epsilon); + assert!(result.is_some()); + let sqrt_result: U110F18 = result.unwrap(); + let precise_sqrt: U110F18 = U110F18::from_num(4.0_f64.sqrt()); + assert!(abs_diff(sqrt_result, precise_sqrt) <= epsilon); + } + + #[test] + fn test_checked_sqrt_large_value() { + let value: U110F18 = U110F18::from_num(1_000_000_000_000_000_000.0); + let epsilon: U110F18 = U110F18::from_num(0.0001); + + let result: Option<U110F18> = checked_sqrt(value, epsilon); + assert!(result.is_some()); + let sqrt_result: U110F18 = result.unwrap(); + let precise_sqrt: U110F18 = U110F18::from_num(1_000_000_000_000_000_000.0_f64.sqrt()); + assert!(abs_diff(sqrt_result, precise_sqrt) <= epsilon); + } + + #[test] + fn test_checked_sqrt_21m_tao_value() { + let value: U110F18 = U110F18::from_num(441_000_000_000_000_000_000_000_000_000_000.0); + let epsilon: U110F18 = U110F18::from_num(1000); + + let result: Option<U110F18> = checked_sqrt(value, epsilon); + assert!(result.is_some()); + let sqrt_result: U110F18 = result.unwrap(); + let precise_sqrt: U110F18 = U110F18::from_num(441_000_000_000_000_000_000_000_000_000_000.0_f64.sqrt()); + assert!(abs_diff(sqrt_result, precise_sqrt) <= epsilon); + } + + #[test] + fn test_checked_sqrt_zero() { + let value: U110F18 = U110F18::from_num(0.0); + let epsilon: U110F18 = U110F18::from_num(0.0001); + + let result: Option<U110F18> = checked_sqrt(value, epsilon); + assert!(result.is_some()); + assert_eq!(result.unwrap(), U110F18::from_num(0.0)); + } + + #[test] + fn test_checked_sqrt_precision() { + let value: U110F18 = U110F18::from_num(2.0); + let epsilon: U110F18 = U110F18::from_num(0.0001); + + let result: Option<U110F18> = checked_sqrt(value, epsilon); + assert!(result.is_some()); + let sqrt_result: U110F18 = result.unwrap(); + let precise_sqrt: U110F18 = U110F18::from_num(2.0_f64.sqrt()); + assert!(abs_diff(sqrt_result, precise_sqrt) <= epsilon); + } + + #[test] + fn test_checked_sqrt_max_iterations() { + let value: U110F18 = U110F18::from_num(2.0); + let epsilon: U110F18 = U110F18::from_num(1e-30); // Very high precision + let result: Option<U110F18> = checked_sqrt(value, epsilon); + assert!(result.is_some()); // Check that it doesn't break, but may not be highly accurate + } +} From fd77135eb34faa5b8f113066f5b21aa8118c6338 Mon Sep 17 00:00:00 2001 From: Greg Zaitsev <gregz@opentensor.dev> Date: Tue, 28 Jan 2025 15:38:32 -0500 Subject: [PATCH 116/145] Change type for pool k to I110F18 --- pallets/subtensor/src/staking/stake_utils.rs | 29 +++++++-------- pallets/subtensor/src/tests/staking.rs | 39 ++++++++++++++++++++ 2 files changed, 53 insertions(+), 15 deletions(-) diff --git a/pallets/subtensor/src/staking/stake_utils.rs b/pallets/subtensor/src/staking/stake_utils.rs index 805b61e50..e247f7f4f 100644 --- a/pallets/subtensor/src/staking/stake_utils.rs +++ b/pallets/subtensor/src/staking/stake_utils.rs @@ -2,7 +2,7 @@ use super::*; use safe_math::*; use share_pool::{SharePool, SharePoolDataOperations}; use sp_std::ops::Neg; -use substrate_fixed::types::{I64F64, I96F32, U64F64}; +use substrate_fixed::types::{I110F18, I64F64, I96F32, U64F64}; impl<T: Config> Pallet<T> { /// Retrieves the total alpha issuance for a given subnet. @@ -469,16 +469,15 @@ impl<T: Config> Pallet<T> { // Step 2: Initialized vars. if mechanism_id == 1 { // Step 3.a.1: Dynamic mechanism calculations - let tao_reserves: I96F32 = I96F32::saturating_from_num(SubnetTAO::<T>::get(netuid)); - let alpha_reserves: I96F32 = - I96F32::saturating_from_num(SubnetAlphaIn::<T>::get(netuid)); + let tao_reserves: I110F18 = I110F18::saturating_from_num(SubnetTAO::<T>::get(netuid)); + let alpha_reserves: I110F18 = + I110F18::saturating_from_num(SubnetAlphaIn::<T>::get(netuid)); // Step 3.a.2: Compute constant product k = alpha * tao - let k: I96F32 = alpha_reserves.saturating_mul(tao_reserves); + let k: I110F18 = alpha_reserves.saturating_mul(tao_reserves); // Calculate new alpha reserve - let new_alpha_reserves: I96F32 = k - .checked_div(tao_reserves.saturating_add(I96F32::saturating_from_num(tao))) - .unwrap_or(I96F32::saturating_from_num(0)); + let new_alpha_reserves: I110F18 = + k.safe_div(tao_reserves.saturating_add(I110F18::saturating_from_num(tao))); // Step 3.a.3: Calculate alpha staked using the constant product formula // alpha_stake_recieved = current_alpha - (k / (current_tao + new_tao)) @@ -509,16 +508,16 @@ impl<T: Config> Pallet<T> { // Step 2: Swap alpha and attain tao if mechanism_id == 1 { // Step 3.a.1: Dynamic mechanism calculations - let tao_reserves: I96F32 = I96F32::saturating_from_num(SubnetTAO::<T>::get(netuid)); - let alpha_reserves: I96F32 = - I96F32::saturating_from_num(SubnetAlphaIn::<T>::get(netuid)); + let tao_reserves: I110F18 = I110F18::saturating_from_num(SubnetTAO::<T>::get(netuid)); + let alpha_reserves: I110F18 = + I110F18::saturating_from_num(SubnetAlphaIn::<T>::get(netuid)); // Step 3.a.2: Compute constant product k = alpha * tao - let k: I96F32 = alpha_reserves.saturating_mul(tao_reserves); + let k: I110F18 = alpha_reserves.saturating_mul(tao_reserves); // Calculate new tao reserve - let new_tao_reserves: I96F32 = k - .checked_div(alpha_reserves.saturating_add(I96F32::saturating_from_num(alpha))) - .unwrap_or(I96F32::saturating_from_num(0)); + let new_tao_reserves: I110F18 = k + .checked_div(alpha_reserves.saturating_add(I110F18::saturating_from_num(alpha))) + .unwrap_or(I110F18::saturating_from_num(0)); // Step 3.a.3: Calculate alpha staked using the constant product formula // tao_recieved = tao_reserves - (k / (alpha_reserves + new_tao)) diff --git a/pallets/subtensor/src/tests/staking.rs b/pallets/subtensor/src/tests/staking.rs index a570175c9..b678968df 100644 --- a/pallets/subtensor/src/tests/staking.rs +++ b/pallets/subtensor/src/tests/staking.rs @@ -2309,3 +2309,42 @@ fn test_unstake_low_liquidity_validate() { ); }); } + +#[test] +fn test_stake_oveflow() { + new_test_ext(1).execute_with(|| { + let subnet_owner_coldkey = U256::from(1001); + let subnet_owner_hotkey = U256::from(1002); + let coldkey_account_id = U256::from(435445); + let hotkey_account_id = U256::from(54544); + let netuid = add_dynamic_network(&subnet_owner_hotkey, &subnet_owner_coldkey); + let amount = 21_000_000_000_000_000; // Max TAO supply + let fee = DefaultStakingFee::<Test>::get(); + register_ok_neuron(netuid, hotkey_account_id, coldkey_account_id, 192213123); + + // Give it some $$$ in his coldkey balance + SubtensorModule::add_balance_to_coldkey_account(&coldkey_account_id, amount); + + // Setup liquidity with 21M TAO values + SubnetTAO::<Test>::insert(netuid, amount); + SubnetAlphaIn::<Test>::insert(netuid, amount); + + // Stake and check if the result is ok + assert_ok!(SubtensorModule::add_stake( + RuntimeOrigin::signed(coldkey_account_id), + hotkey_account_id, + netuid, + amount + )); + + // Check if stake has increased properly (staking 1:1 to SubnetTAO results in SubnetTAO/2 alpha) + assert_abs_diff_eq!( + SubtensorModule::get_stake_for_hotkey_on_subnet(&hotkey_account_id, netuid), + (amount - fee) / 2, + epsilon = amount / 1_000_000, + ); + + // Check if total stake has increased accordingly. + assert_abs_diff_eq!(SubtensorModule::get_total_stake(), amount, epsilon = 10,); + }); +} From af3fcf77cd2ed291eb7c1742f5f496a5b97a4e33 Mon Sep 17 00:00:00 2001 From: Greg Zaitsev <gregz@opentensor.dev> Date: Tue, 28 Jan 2025 16:09:29 -0500 Subject: [PATCH 117/145] Raise minimum pool liquidity to 1 TAO --- pallets/subtensor/src/lib.rs | 2 +- pallets/subtensor/src/tests/staking2.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pallets/subtensor/src/lib.rs b/pallets/subtensor/src/lib.rs index 7f9cd7120..65bd1cd71 100644 --- a/pallets/subtensor/src/lib.rs +++ b/pallets/subtensor/src/lib.rs @@ -736,7 +736,7 @@ pub mod pallet { #[pallet::type_value] /// Default value for minimum liquidity in pool pub fn DefaultMinimumPoolLiquidity<T: Config>() -> I96F32 { - I96F32::saturating_from_num(1_000_000) + I96F32::saturating_from_num(1_000_000_000) } #[pallet::storage] diff --git a/pallets/subtensor/src/tests/staking2.rs b/pallets/subtensor/src/tests/staking2.rs index bb71c8594..2abff8faf 100644 --- a/pallets/subtensor/src/tests/staking2.rs +++ b/pallets/subtensor/src/tests/staking2.rs @@ -15,7 +15,7 @@ fn test_stake_base_case() { // Initialize subnet with some existing TAO and Alpha let initial_subnet_tao = 10_000_000_000; // 10 TAO - let initial_subnet_alpha = 5_000_000; // 5 Alpha + let initial_subnet_alpha = 5_000_000_000; // 5 Alpha SubnetTAO::<Test>::insert(netuid, initial_subnet_tao); SubnetAlphaIn::<Test>::insert(netuid, initial_subnet_alpha); SubnetAlphaOut::<Test>::insert(netuid, initial_subnet_alpha); From baf4a95721c9a00499245605ebd6b536f4407426 Mon Sep 17 00:00:00 2001 From: Greg Zaitsev <gregz@opentensor.dev> Date: Tue, 28 Jan 2025 16:48:41 -0500 Subject: [PATCH 118/145] Format --- pallets/subtensor/src/staking/add_stake.rs | 2 +- pallets/subtensor/src/tests/staking.rs | 2 +- primitives/safe-math/src/lib.rs | 8 ++++++-- 3 files changed, 8 insertions(+), 4 deletions(-) diff --git a/pallets/subtensor/src/staking/add_stake.rs b/pallets/subtensor/src/staking/add_stake.rs index 291d26a30..d6c49686a 100644 --- a/pallets/subtensor/src/staking/add_stake.rs +++ b/pallets/subtensor/src/staking/add_stake.rs @@ -180,7 +180,7 @@ impl<T: Config> Pallet<T> { limit_price_float .saturating_mul(tao_reserve_float) .saturating_mul(alpha_in_float), - U96F32::saturating_from_num(0.1), + U96F32::saturating_from_num(0.1), ) .unwrap_or(zero); diff --git a/pallets/subtensor/src/tests/staking.rs b/pallets/subtensor/src/tests/staking.rs index 0506fc82d..c5fe0578f 100644 --- a/pallets/subtensor/src/tests/staking.rs +++ b/pallets/subtensor/src/tests/staking.rs @@ -2458,4 +2458,4 @@ fn test_max_amount_add_dynamic() { assert!(SubtensorModule::get_max_amount_add(netuid, u64::MAX - 1) < 21_000_000_000_000_000); assert!(SubtensorModule::get_max_amount_add(netuid, u64::MAX / 2) < 21_000_000_000_000_000); }); -} \ No newline at end of file +} diff --git a/primitives/safe-math/src/lib.rs b/primitives/safe-math/src/lib.rs index 450b7a203..af27b738b 100644 --- a/primitives/safe-math/src/lib.rs +++ b/primitives/safe-math/src/lib.rs @@ -1,7 +1,10 @@ #![cfg_attr(not(feature = "std"), no_std)] #![allow(clippy::result_unit_err)] -use substrate_fixed::{traits::Fixed, types::{I110F18, I32F32, I64F64, I96F32, U110F18, U64F64, U96F32}}; +use substrate_fixed::{ + traits::Fixed, + types::{I110F18, I32F32, I64F64, I96F32, U110F18, U64F64, U96F32}, +}; /// Safe division trait pub trait SafeDiv { @@ -138,7 +141,8 @@ mod tests { let result: Option<U110F18> = checked_sqrt(value, epsilon); assert!(result.is_some()); let sqrt_result: U110F18 = result.unwrap(); - let precise_sqrt: U110F18 = U110F18::from_num(441_000_000_000_000_000_000_000_000_000_000.0_f64.sqrt()); + let precise_sqrt: U110F18 = + U110F18::from_num(441_000_000_000_000_000_000_000_000_000_000.0_f64.sqrt()); assert!(abs_diff(sqrt_result, precise_sqrt) <= epsilon); } From bffbcd78727868a04eb50efd400450b0260412d1 Mon Sep 17 00:00:00 2001 From: Greg Zaitsev <gregz@opentensor.dev> Date: Tue, 28 Jan 2025 18:26:02 -0500 Subject: [PATCH 119/145] Implement remove_stake_limit --- pallets/subtensor/src/staking/add_stake.rs | 19 +- pallets/subtensor/src/staking/remove_stake.rs | 104 ++++++- pallets/subtensor/src/tests/staking.rs | 265 +++++++++++++++++- primitives/safe-math/src/lib.rs | 8 +- 4 files changed, 363 insertions(+), 33 deletions(-) diff --git a/pallets/subtensor/src/staking/add_stake.rs b/pallets/subtensor/src/staking/add_stake.rs index d6c49686a..fd4ec630a 100644 --- a/pallets/subtensor/src/staking/add_stake.rs +++ b/pallets/subtensor/src/staking/add_stake.rs @@ -79,7 +79,7 @@ impl<T: Config> Pallet<T> { /// - The amount of stake to be added to the hotkey staking account. /// /// * 'limit_price' (u64): - /// - The limit price expressed in units of RAO per one Alpha. + /// - The limit price expressed in units of RAO per one Alpha. /// /// # Event: /// * StakeAdded; @@ -176,16 +176,13 @@ impl<T: Config> Pallet<T> { // This is the positive solution of quare equation for finding additional TAO from // limit_price. let zero: U96F32 = U96F32::saturating_from_num(0.0); - let sqrt: U96F32 = checked_sqrt( - limit_price_float - .saturating_mul(tao_reserve_float) - .saturating_mul(alpha_in_float), - U96F32::saturating_from_num(0.1), - ) - .unwrap_or(zero); - - U96F32::saturating_from_num(sqrt) - .saturating_sub(U96F32::saturating_from_num(tao_reserve_float)) + let epsilon: U96F32 = U96F32::saturating_from_num(0.1); + let sqrt: U96F32 = + checked_sqrt(limit_price_float.saturating_mul(tao_reserve_float), epsilon) + .unwrap_or(zero) + .saturating_mul(checked_sqrt(alpha_in_float, epsilon).unwrap_or(zero)); + + sqrt.saturating_sub(U96F32::saturating_from_num(tao_reserve_float)) .saturating_to_num::<u64>() } } diff --git a/pallets/subtensor/src/staking/remove_stake.rs b/pallets/subtensor/src/staking/remove_stake.rs index 59ff7e5b3..564a62f78 100644 --- a/pallets/subtensor/src/staking/remove_stake.rs +++ b/pallets/subtensor/src/staking/remove_stake.rs @@ -1,5 +1,7 @@ use super::*; +use safe_math::*; use sp_core::Get; +use substrate_fixed::types::U96F32; impl<T: Config> Pallet<T> { /// ---- The implementation for the extrinsic remove_stake: Removes stake from a hotkey account and adds it onto a coldkey. @@ -222,23 +224,107 @@ impl<T: Config> Pallet<T> { } pub fn do_remove_stake_limit( - _origin: T::RuntimeOrigin, - _hotkey: T::AccountId, + origin: T::RuntimeOrigin, + hotkey: T::AccountId, netuid: u16, - _stake_to_be_added: u64, + alpha_unstaked: u64, limit_price: u64, ) -> dispatch::DispatchResult { - // TODO: Do all checks + // 1. We check the transaction is signed by the caller and retrieve the T::AccountId coldkey information. + let coldkey = ensure_signed(origin)?; + log::info!( + "do_remove_stake( origin:{:?} hotkey:{:?}, netuid: {:?}, alpha_unstaked:{:?} )", + coldkey, + hotkey, + netuid, + alpha_unstaked + ); + + // 2. Validate the user input + Self::validate_remove_stake(&coldkey, &hotkey, netuid, alpha_unstaked)?; + + // 3. Calcaulate the maximum amount that can be executed with price limit + let max_amount = Self::get_max_amount_remove(netuid, limit_price); + let mut possible_alpha = alpha_unstaked; + if possible_alpha > max_amount { + possible_alpha = max_amount; + } + + // 4. Swap the alpba to tao and update counters for this subnet. + let fee = DefaultStakingFee::<T>::get(); + let tao_unstaked: u64 = + Self::unstake_from_subnet(&hotkey, &coldkey, netuid, possible_alpha, fee); + + // 5. We add the balance to the coldkey. If the above fails we will not credit this coldkey. + Self::add_balance_to_coldkey_account(&coldkey, tao_unstaked); - // Calcaulate the maximum amount that can be executed with price limit - let _max_amount = Self::get_max_amount_remove(netuid, limit_price); + // 6. If the stake is below the minimum, we clear the nomination from storage. + Self::clear_small_nomination_if_required(&hotkey, &coldkey, netuid); - // Ok and return. + // 7. Check if stake lowered below MinStake and remove Pending children if it did + if Self::get_total_stake_for_hotkey(&hotkey) < StakeThreshold::<T>::get() { + Self::get_all_subnet_netuids().iter().for_each(|netuid| { + PendingChildKeys::<T>::remove(netuid, &hotkey); + }) + } + + // Done and ok. Ok(()) } // Returns the maximum amount of RAO that can be executed with price limit - pub fn get_max_amount_remove(_netuid: u16, _limit_price: u64) -> u64 { - 0 + pub fn get_max_amount_remove(netuid: u16, limit_price: u64) -> u64 { + // Corner case: root and stao + // There's no slippage for root or stable subnets, so if limit price is 1e9 rao or + // higher, then max_amount equals u64::MAX, otherwise it is 0. + if (netuid == Self::get_root_netuid()) || (SubnetMechanism::<T>::get(netuid)) == 0 { + if limit_price <= 1_000_000_000 { + return u64::MAX; + } else { + return 0; + } + } + + // Corner case: SubnetAlphaIn is zero. Staking can't happen, so max amount is zero. + let alpha_in = SubnetAlphaIn::<T>::get(netuid); + if alpha_in == 0 { + return 0; + } + let alpha_in_float: U96F32 = U96F32::saturating_from_num(alpha_in); + + // Corner case: SubnetTAO is zero. Staking can't happen, so max amount is zero. + let tao_reserve = SubnetTAO::<T>::get(netuid); + if tao_reserve == 0 { + return 0; + } + let tao_reserve_float: U96F32 = U96F32::saturating_from_num(tao_reserve); + + // Corner case: limit_price == 0 (because there's division by limit price) + // => can sell all + if limit_price == 0 { + return u64::MAX; + } + + // Corner case: limit_price > current_price (price cannot increase with unstaking) + let limit_price_float: U96F32 = U96F32::saturating_from_num(limit_price) + .checked_div(U96F32::saturating_from_num(1_000_000_000)) + .unwrap_or(U96F32::saturating_from_num(0)); + if limit_price_float > Self::get_alpha_price(netuid) { + return 0; + } + + // Main case: return SQRT(SubnetTAO * SubnetAlphaIn / limit_price) - SubnetAlphaIn + // This is the positive solution of quare equation for finding Alpha amount from + // limit_price. + let zero: U96F32 = U96F32::saturating_from_num(0.0); + let epsilon: U96F32 = U96F32::saturating_from_num(0.1); + let sqrt: U96F32 = checked_sqrt(tao_reserve_float, epsilon) + .unwrap_or(zero) + .saturating_mul( + checked_sqrt(alpha_in_float.safe_div(limit_price_float), epsilon).unwrap_or(zero), + ); + + sqrt.saturating_sub(U96F32::saturating_from_num(alpha_in_float)) + .saturating_to_num::<u64>() } } diff --git a/pallets/subtensor/src/tests/staking.rs b/pallets/subtensor/src/tests/staking.rs index c5fe0578f..0c5f8d526 100644 --- a/pallets/subtensor/src/tests/staking.rs +++ b/pallets/subtensor/src/tests/staking.rs @@ -145,7 +145,7 @@ fn test_add_stake_err_signature() { let netuid = 1; assert_err!( - SubtensorModule::add_stake(RawOrigin::None.into(), hotkey_account_id, netuid, amount,), + SubtensorModule::add_stake(RawOrigin::None.into(), hotkey_account_id, netuid, amount), DispatchError::BadOrigin ); }); @@ -2241,7 +2241,7 @@ fn test_stake_overflow() { ); // Check if total stake has increased accordingly. - assert_abs_diff_eq!(SubtensorModule::get_total_stake(), amount, epsilon = 10,); + assert_abs_diff_eq!(SubtensorModule::get_total_stake(), amount, epsilon = 10); }); } @@ -2411,6 +2411,7 @@ fn test_max_amount_add_stable() { }); } +// cargo test --package pallet-subtensor --lib -- tests::staking::test_max_amount_add_dynamic --exact --show-output #[test] fn test_max_amount_add_dynamic() { new_test_ext(0).execute_with(|| { @@ -2442,16 +2443,15 @@ fn test_max_amount_add_dynamic() { ); // 4x price => max is 1x TAO - assert_eq!( + assert_abs_diff_eq!( SubtensorModule::get_max_amount_add(netuid, 6_000_000_000), - 150_000_000_000 + 150_000_000_000, + epsilon = 10_000, ); - // 1.50000....1 price => max is 46 rao - assert_eq!( - SubtensorModule::get_max_amount_add(netuid, 1_500_000_001), - 46 - ); + // Precision test: + // 1.50000..100 price => max > 0 + assert!(SubtensorModule::get_max_amount_add(netuid, 1_500_000_100) > 0); // Max price doesn't panic and returns something meaningful assert!(SubtensorModule::get_max_amount_add(netuid, u64::MAX) < 21_000_000_000_000_000); @@ -2459,3 +2459,250 @@ fn test_max_amount_add_dynamic() { assert!(SubtensorModule::get_max_amount_add(netuid, u64::MAX / 2) < 21_000_000_000_000_000); }); } + +#[test] +fn test_max_amount_remove_root() { + new_test_ext(0).execute_with(|| { + // 0 price on root => max is 0 + assert_eq!(SubtensorModule::get_max_amount_remove(0, 0), 0); + + // 0.999999... price on root => max is 0 + assert_eq!(SubtensorModule::get_max_amount_remove(0, 999_999_999), 0); + + // 1.0 price on root => max is u64::MAX + assert_eq!( + SubtensorModule::get_max_amount_remove(0, 1_000_000_000), + u64::MAX + ); + + // 1.000...001 price on root => max is u64::MAX + assert_eq!( + SubtensorModule::get_max_amount_remove(0, 1_000_000_001), + u64::MAX + ); + + // 2.0 price on root => max is u64::MAX + assert_eq!( + SubtensorModule::get_max_amount_remove(0, 2_000_000_000), + u64::MAX + ); + }); +} + +#[test] +fn test_max_amount_remove_stable() { + new_test_ext(0).execute_with(|| { + let netuid: u16 = 1; + add_network(netuid, 1, 0); + + // 0 price => max is 0 + assert_eq!(SubtensorModule::get_max_amount_remove(netuid, 0), 0); + + // 0.999999... price => max is 0 + assert_eq!( + SubtensorModule::get_max_amount_remove(netuid, 999_999_999), + 0 + ); + + // 1.0 price => max is u64::MAX + assert_eq!( + SubtensorModule::get_max_amount_remove(netuid, 1_000_000_000), + u64::MAX + ); + + // 1.000...001 price => max is u64::MAX + assert_eq!( + SubtensorModule::get_max_amount_remove(netuid, 1_000_000_001), + u64::MAX + ); + + // 2.0 price => max is u64::MAX + assert_eq!( + SubtensorModule::get_max_amount_remove(netuid, 2_000_000_000), + u64::MAX + ); + }); +} + +// cargo test --package pallet-subtensor --lib -- tests::staking::test_max_amount_remove_dynamic --exact --show-output +#[test] +fn test_max_amount_remove_dynamic() { + new_test_ext(0).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); + + // Forse-set alpha in and tao reserve to make price equal 1.5 + let tao_reserve: U96F32 = U96F32::from_num(150_000_000_000_u64); + let alpha_in: U96F32 = U96F32::from_num(100_000_000_000_u64); + SubnetTAO::<Test>::insert(netuid, tao_reserve.to_num::<u64>()); + SubnetAlphaIn::<Test>::insert(netuid, alpha_in.to_num::<u64>()); + let current_price: U96F32 = U96F32::from_num(SubtensorModule::get_alpha_price(netuid)); + assert_eq!(current_price, U96F32::from_num(1.5)); + + // 0 price => max is u64::MAX + assert_eq!(SubtensorModule::get_max_amount_remove(netuid, 0), u64::MAX); + + // Low price values don't blow things up + assert!(SubtensorModule::get_max_amount_remove(netuid, 1) > 0); + assert!(SubtensorModule::get_max_amount_remove(netuid, 2) > 0); + assert!(SubtensorModule::get_max_amount_remove(netuid, 3) > 0); + + // 1.5000...1 price => max is 0 + assert_eq!( + SubtensorModule::get_max_amount_remove(netuid, 1_500_000_001), + 0 + ); + + // 1.5 price => max is 0 because of non-zero slippage + assert_abs_diff_eq!( + SubtensorModule::get_max_amount_remove(netuid, 1_500_000_000), + 0, + epsilon = 10_000 + ); + + // 1/4 price => max is 2x Alpha + assert_abs_diff_eq!( + SubtensorModule::get_max_amount_remove(netuid, 375_000_000), + 100_000_000_000, + epsilon = 10_000, + ); + + // Precision test: + // 1.499999.. price => max > 0 + assert!(SubtensorModule::get_max_amount_remove(netuid, 1_499_999_999) > 0); + + // Max price doesn't panic and returns something meaningful + assert!(SubtensorModule::get_max_amount_remove(netuid, u64::MAX) < 21_000_000_000_000_000); + assert!( + SubtensorModule::get_max_amount_remove(netuid, u64::MAX - 1) < 21_000_000_000_000_000 + ); + assert!( + SubtensorModule::get_max_amount_remove(netuid, u64::MAX / 2) < 21_000_000_000_000_000 + ); + }); +} + +#[test] +fn test_add_stake_limit_ok() { + new_test_ext(1).execute_with(|| { + let hotkey_account_id = U256::from(533453); + let coldkey_account_id = U256::from(55453); + let amount = 300_000_000_000; + let fee = DefaultStakingFee::<Test>::get(); + + // add network + let netuid: u16 = add_dynamic_network(&hotkey_account_id, &coldkey_account_id); + + // Forse-set alpha in and tao reserve to make price equal 1.5 + let tao_reserve: U96F32 = U96F32::from_num(150_000_000_000_u64); + let alpha_in: U96F32 = U96F32::from_num(100_000_000_000_u64); + SubnetTAO::<Test>::insert(netuid, tao_reserve.to_num::<u64>()); + SubnetAlphaIn::<Test>::insert(netuid, alpha_in.to_num::<u64>()); + let current_price: U96F32 = U96F32::from_num(SubtensorModule::get_alpha_price(netuid)); + assert_eq!(current_price, U96F32::from_num(1.5)); + + // Give it some $$$ in his coldkey balance + SubtensorModule::add_balance_to_coldkey_account(&coldkey_account_id, amount); + + // Setup limit price so that it doesn't peak above 4x of current price + // The amount that can be executed at this price is 150 TAO only + // Alpha produced will be equal to 50 = 100 - 150*100/300 + let limit_price = 6_000_000_000; + let expected_executed_stake = 50_000_000_000; + + // Add stake with slippage safety and check if the result is ok + assert_ok!(SubtensorModule::add_stake_limit( + RuntimeOrigin::signed(coldkey_account_id), + hotkey_account_id, + netuid, + amount, + limit_price + )); + + // Check if stake has increased only by 50 Alpha + assert_abs_diff_eq!( + SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( + &hotkey_account_id, + &coldkey_account_id, + netuid + ), + expected_executed_stake - fee, + epsilon = expected_executed_stake / 1000, + ); + + // Check that 150 TAO balance still remains free on coldkey + assert_abs_diff_eq!( + SubtensorModule::get_coldkey_balance(&coldkey_account_id), + 150_000_000_000, + epsilon = 10_000 + ); + + // Check that price has updated to ~6 + let exp_price = U96F32::from_num(6.0); + let current_price: U96F32 = U96F32::from_num(SubtensorModule::get_alpha_price(netuid)); + assert!(exp_price.saturating_sub(current_price) < 0.0001); + assert!(current_price.saturating_sub(exp_price) < 0.0001); + }); +} + +#[test] +fn test_remove_stake_limit_ok() { + new_test_ext(1).execute_with(|| { + let hotkey_account_id = U256::from(533453); + let coldkey_account_id = U256::from(55453); + let stake_amount = 300_000_000_000; + let unstake_amount = 150_000_000_000; + let fee = DefaultStakingFee::<Test>::get(); + + // add network + let netuid: u16 = add_dynamic_network(&hotkey_account_id, &coldkey_account_id); + + // Give the neuron some stake to remove + SubtensorModule::increase_stake_for_hotkey_and_coldkey_on_subnet( + &hotkey_account_id, + &coldkey_account_id, + netuid, + stake_amount, + ); + let alpha_before = SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( + &hotkey_account_id, + &coldkey_account_id, + netuid, + ); + + // Forse-set alpha in and tao reserve to make price equal 1.5 + let tao_reserve: U96F32 = U96F32::from_num(150_000_000_000_u64); + let alpha_in: U96F32 = U96F32::from_num(100_000_000_000_u64); + SubnetTAO::<Test>::insert(netuid, tao_reserve.to_num::<u64>()); + SubnetAlphaIn::<Test>::insert(netuid, alpha_in.to_num::<u64>()); + let current_price: U96F32 = U96F32::from_num(SubtensorModule::get_alpha_price(netuid)); + assert_eq!(current_price, U96F32::from_num(1.5)); + + // Setup limit price so that it doesn't drop by more than 10% from current price + let limit_price = 1_350_000_000; + + // Alpha unstaked = sqrt(150 * 100 / 1.35) - 100 ~ 5.409 + let expected_alpha_reduction = 5_409_000_000; + + // Remove stake with slippage safety + assert_ok!(SubtensorModule::remove_stake_limit( + RuntimeOrigin::signed(coldkey_account_id), + hotkey_account_id, + netuid, + unstake_amount, + limit_price + )); + + // Check if stake has decreased only by + assert_abs_diff_eq!( + SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( + &hotkey_account_id, + &coldkey_account_id, + netuid + ), + alpha_before - expected_alpha_reduction - fee, + epsilon = expected_alpha_reduction / 1_000, + ); + }); +} diff --git a/primitives/safe-math/src/lib.rs b/primitives/safe-math/src/lib.rs index af27b738b..da68e819c 100644 --- a/primitives/safe-math/src/lib.rs +++ b/primitives/safe-math/src/lib.rs @@ -69,9 +69,9 @@ pub fn checked_sqrt<T: SafeDiv + Fixed>(value: T, epsilon: T) -> Option<T> { let mut high: T = value; let mut low: T = zero; - let mut middle: T = (high + low) / two; + let mut middle: T = high.saturating_add(low).safe_div(two); - let mut iteration = 0; + let mut iteration: i32 = 0; let max_iterations = 128; let mut check_val: T = value.safe_div(middle); @@ -83,10 +83,10 @@ pub fn checked_sqrt<T: SafeDiv + Fixed>(value: T, epsilon: T) -> Option<T> { low = middle; } - middle = (high + low) / two; + middle = high.saturating_add(low).safe_div(two); check_val = value.safe_div(middle); - iteration += 1; + iteration = iteration.saturating_add(1); if iteration > max_iterations { break; } From 0a99ee9167ab0eedb2b87f10a3c7e562858e849d Mon Sep 17 00:00:00 2001 From: Greg Zaitsev <gregz@opentensor.dev> Date: Tue, 28 Jan 2025 18:47:33 -0500 Subject: [PATCH 120/145] Fix clippy --- primitives/safe-math/src/lib.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/primitives/safe-math/src/lib.rs b/primitives/safe-math/src/lib.rs index da68e819c..e73aeb003 100644 --- a/primitives/safe-math/src/lib.rs +++ b/primitives/safe-math/src/lib.rs @@ -1,5 +1,7 @@ #![cfg_attr(not(feature = "std"), no_std)] #![allow(clippy::result_unit_err)] +#![cfg_attr(test, allow(clippy::arithmetic_side_effects))] +#![cfg_attr(test, allow(clippy::unwrap_used))] use substrate_fixed::{ traits::Fixed, From 1bc4d6eeb05dabb53fc1d0c5843057a4a43052ca Mon Sep 17 00:00:00 2001 From: Greg Zaitsev <gregz@opentensor.dev> Date: Tue, 28 Jan 2025 19:00:27 -0500 Subject: [PATCH 121/145] Fix tests --- pallets/subtensor/src/tests/staking.rs | 41 ++++++++++++++------------ 1 file changed, 22 insertions(+), 19 deletions(-) diff --git a/pallets/subtensor/src/tests/staking.rs b/pallets/subtensor/src/tests/staking.rs index 0c5f8d526..51887a17d 100644 --- a/pallets/subtensor/src/tests/staking.rs +++ b/pallets/subtensor/src/tests/staking.rs @@ -2463,29 +2463,32 @@ fn test_max_amount_add_dynamic() { #[test] fn test_max_amount_remove_root() { new_test_ext(0).execute_with(|| { - // 0 price on root => max is 0 - assert_eq!(SubtensorModule::get_max_amount_remove(0, 0), 0); - - // 0.999999... price on root => max is 0 - assert_eq!(SubtensorModule::get_max_amount_remove(0, 999_999_999), 0); + // 0 price on root => max is u64::MAX + assert_eq!(SubtensorModule::get_max_amount_remove(0, 0), u64::MAX); - // 1.0 price on root => max is u64::MAX + // 0.5 price on root => max is u64::MAX assert_eq!( - SubtensorModule::get_max_amount_remove(0, 1_000_000_000), + SubtensorModule::get_max_amount_remove(0, 500_000_000), u64::MAX ); - // 1.000...001 price on root => max is u64::MAX + // 0.999999... price on root => max is u64::MAX assert_eq!( - SubtensorModule::get_max_amount_remove(0, 1_000_000_001), + SubtensorModule::get_max_amount_remove(0, 999_999_999), u64::MAX ); - // 2.0 price on root => max is u64::MAX + // 1.0 price on root => max is u64::MAX assert_eq!( - SubtensorModule::get_max_amount_remove(0, 2_000_000_000), + SubtensorModule::get_max_amount_remove(0, 1_000_000_000), u64::MAX ); + + // 1.000...001 price on root => max is 0 + assert_eq!(SubtensorModule::get_max_amount_remove(0, 1_000_000_001), 0); + + // 2.0 price on root => max is 0 + assert_eq!(SubtensorModule::get_max_amount_remove(0, 2_000_000_000), 0); }); } @@ -2495,13 +2498,13 @@ fn test_max_amount_remove_stable() { let netuid: u16 = 1; add_network(netuid, 1, 0); - // 0 price => max is 0 - assert_eq!(SubtensorModule::get_max_amount_remove(netuid, 0), 0); + // 0 price => max is u64::MAX + assert_eq!(SubtensorModule::get_max_amount_remove(netuid, 0), u64::MAX); - // 0.999999... price => max is 0 + // 0.999999... price => max is u64::MAX assert_eq!( SubtensorModule::get_max_amount_remove(netuid, 999_999_999), - 0 + u64::MAX ); // 1.0 price => max is u64::MAX @@ -2510,16 +2513,16 @@ fn test_max_amount_remove_stable() { u64::MAX ); - // 1.000...001 price => max is u64::MAX + // 1.000...001 price => max is 0 assert_eq!( SubtensorModule::get_max_amount_remove(netuid, 1_000_000_001), - u64::MAX + 0 ); - // 2.0 price => max is u64::MAX + // 2.0 price => max is 0 assert_eq!( SubtensorModule::get_max_amount_remove(netuid, 2_000_000_000), - u64::MAX + 0 ); }); } From 06ba8e57fccdc9b4e152711e355026d317349ee4 Mon Sep 17 00:00:00 2001 From: Aliaksandr Tsurko <ales@opentensor.dev> Date: Wed, 29 Jan 2025 14:50:06 +0100 Subject: [PATCH 122/145] Add an option to run try-runtime script with a snapshot --- scripts/try-runtime-upgrade.sh | 33 +++++++++++++++++++++++++++------ 1 file changed, 27 insertions(+), 6 deletions(-) diff --git a/scripts/try-runtime-upgrade.sh b/scripts/try-runtime-upgrade.sh index 7c3e5fe77..80495bcb1 100755 --- a/scripts/try-runtime-upgrade.sh +++ b/scripts/try-runtime-upgrade.sh @@ -3,7 +3,7 @@ # Tries runtime upgrade (via try-runtime). # # Usage: -# try-runtime-upgrade.sh [-p <runtime-path>] [-u <live-chain-url>] +# try-runtime-upgrade.sh [-p <runtime-path>] [-u <live-chain-url>] [-s <snapshot-path>] # # Dependencies: # - rust toolchain @@ -13,15 +13,28 @@ set -eou pipefail runtime_wasm_path="./target/release/wbuild/node-subtensor-runtime/node_subtensor_runtime.compact.wasm" live_chain_url="wss://dev.chain.opentensor.ai:443" +snapshot_path="" parse_args() { - while getopts "p:u:" opt; do + u_provided=false + + while getopts "r:u:s:" opt; do case "${opt}" in - p) runtime_wasm_path="${OPTARG}" ;; - u) live_chain_url="${OPTARG}" ;; - *) echo "Usage: $(basename "$0") [-p <runtime-path>] [-u <live-chain-url>]" && exit 1 ;; + r) runtime_wasm_path="${OPTARG}" ;; + u) + live_chain_url="${OPTARG}" + u_provided=true + ;; + s) snapshot_path="${OPTARG}" ;; + *) echo "Usage: $(basename "$0") [-r <runtime-path>] [-u <live-chain-url>] [-s <snapshot-path>]" && exit 1 ;; esac done + + # Prevent specifying URI if snapshot is specified + if [ -n "$snapshot_path" ] && [ "$u_provided" = true ]; then + echo "Error: Either live URI or snapshot path should be specified, but not both." + exit 1 + fi } build_runtime() { @@ -29,7 +42,15 @@ build_runtime() { } do_try_runtime() { - try-runtime --runtime "$runtime_wasm_path" on-runtime-upgrade live --uri "$live_chain_url" + if [ -n "$snapshot_path" ]; then + chain_state="snap --path $snapshot_path" + else + chain_state="live --uri $live_chain_url" + fi + + eval "try-runtime --runtime $runtime_wasm_path on-runtime-upgrade \ + --no-weight-warnings --disable-spec-version-check --disable-idempotency-checks \ + $chain_state" } parse_args "$@" From 3678d4c2aee36a0984f552bb89694ef95e094428 Mon Sep 17 00:00:00 2001 From: Aliaksandr Tsurko <ales@opentensor.dev> Date: Wed, 29 Jan 2025 15:08:05 +0100 Subject: [PATCH 123/145] Update spec version --- runtime/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/runtime/src/lib.rs b/runtime/src/lib.rs index 372c9a981..2a2d2fac4 100644 --- a/runtime/src/lib.rs +++ b/runtime/src/lib.rs @@ -220,7 +220,7 @@ pub const VERSION: RuntimeVersion = RuntimeVersion { // `spec_version`, and `authoring_version` are the same between Wasm and native. // This value is set to 100 to notify Polkadot-JS App (https://polkadot.js.org/apps) to use // the compatible custom types. - spec_version: 224, + spec_version: 225, impl_version: 1, apis: RUNTIME_API_VERSIONS, transaction_version: 1, From 79ed9e3d09cf725fa6cd24c9c53bce03e786e5fc Mon Sep 17 00:00:00 2001 From: Greg Zaitsev <gregz@opentensor.dev> Date: Wed, 29 Jan 2025 11:33:05 -0500 Subject: [PATCH 124/145] Add allow_partial parameter for add_stake_limit and remove_stake_limit --- pallets/subtensor/src/lib.rs | 10 ++ pallets/subtensor/src/macros/dispatches.rs | 28 ++++- pallets/subtensor/src/macros/errors.rs | 2 + pallets/subtensor/src/staking/add_stake.rs | 29 ++++- pallets/subtensor/src/staking/remove_stake.rs | 63 +++++++++- pallets/subtensor/src/staking/stake_utils.rs | 16 +++ pallets/subtensor/src/tests/staking.rs | 112 +++++++++++++++++- 7 files changed, 246 insertions(+), 14 deletions(-) diff --git a/pallets/subtensor/src/lib.rs b/pallets/subtensor/src/lib.rs index 7f9cd7120..d346b9241 100644 --- a/pallets/subtensor/src/lib.rs +++ b/pallets/subtensor/src/lib.rs @@ -1569,6 +1569,7 @@ pub enum CustomTransactionError { NotEnoughStakeToWithdraw, RateLimitExceeded, InsufficientLiquidity, + SlippageTooHigh, BadRequest, } @@ -1583,6 +1584,7 @@ impl From<CustomTransactionError> for u8 { CustomTransactionError::NotEnoughStakeToWithdraw => 5, CustomTransactionError::RateLimitExceeded => 6, CustomTransactionError::InsufficientLiquidity => 7, + CustomTransactionError::SlippageTooHigh => 8, CustomTransactionError::BadRequest => 255, } } @@ -1654,6 +1656,10 @@ where CustomTransactionError::InsufficientLiquidity.into(), ) .into()), + Error::<T>::SlippageTooHigh => Err(InvalidTransaction::Custom( + CustomTransactionError::SlippageTooHigh.into(), + ) + .into()), _ => Err( InvalidTransaction::Custom(CustomTransactionError::BadRequest.into()).into(), ), @@ -1801,6 +1807,8 @@ where hotkey, *netuid, *amount_staked, + *amount_staked, + false, )) } Some(Call::remove_stake { @@ -1814,6 +1822,8 @@ where hotkey, *netuid, *amount_unstaked, + *amount_unstaked, + false, )) } Some(Call::move_stake { diff --git a/pallets/subtensor/src/macros/dispatches.rs b/pallets/subtensor/src/macros/dispatches.rs index 2a1165b1f..6b8592db8 100644 --- a/pallets/subtensor/src/macros/dispatches.rs +++ b/pallets/subtensor/src/macros/dispatches.rs @@ -1710,6 +1710,10 @@ mod dispatches { /// * 'limit_price' (u64): /// - The limit price expressed in units of RAO per one Alpha. /// + /// * 'allow_partial' (bool): + /// - Allows partial execution of the amount. If set to false, this becomes + /// fill or kill type or order. + /// /// # Event: /// * StakeAdded; /// - On the successfully adding stake to a global account. @@ -1734,8 +1738,16 @@ mod dispatches { netuid: u16, amount_staked: u64, limit_price: u64, + allow_partial: bool, ) -> DispatchResult { - Self::do_add_stake_limit(origin, hotkey, netuid, amount_staked, limit_price) + Self::do_add_stake_limit( + origin, + hotkey, + netuid, + amount_staked, + limit_price, + allow_partial, + ) } /// --- Removes stake from a hotkey on a subnet with a price limit. @@ -1759,6 +1771,10 @@ mod dispatches { /// * 'limit_price' (u64): /// - The limit price expressed in units of RAO per one Alpha. /// + /// * 'allow_partial' (bool): + /// - Allows partial execution of the amount. If set to false, this becomes + /// fill or kill type or order. + /// /// # Event: /// * StakeRemoved; /// - On the successfully removing stake from the hotkey account. @@ -1784,8 +1800,16 @@ mod dispatches { netuid: u16, amount_unstaked: u64, limit_price: u64, + allow_partial: bool, ) -> DispatchResult { - Self::do_remove_stake_limit(origin, hotkey, netuid, amount_unstaked, limit_price) + Self::do_remove_stake_limit( + origin, + hotkey, + netuid, + amount_unstaked, + limit_price, + allow_partial, + ) } } } diff --git a/pallets/subtensor/src/macros/errors.rs b/pallets/subtensor/src/macros/errors.rs index f1eea3b9c..926aead58 100644 --- a/pallets/subtensor/src/macros/errors.rs +++ b/pallets/subtensor/src/macros/errors.rs @@ -187,5 +187,7 @@ mod errors { AmountTooLow, /// Not enough liquidity. InsufficientLiquidity, + /// Slippage is too high for the transaction. + SlippageTooHigh, } } diff --git a/pallets/subtensor/src/staking/add_stake.rs b/pallets/subtensor/src/staking/add_stake.rs index fd4ec630a..ded1ae18a 100644 --- a/pallets/subtensor/src/staking/add_stake.rs +++ b/pallets/subtensor/src/staking/add_stake.rs @@ -50,7 +50,14 @@ impl<T: Config> Pallet<T> { ); // 2. Validate user input - Self::validate_add_stake(&coldkey, &hotkey, netuid, stake_to_be_added)?; + Self::validate_add_stake( + &coldkey, + &hotkey, + netuid, + stake_to_be_added, + stake_to_be_added, + false, + )?; // 3. Ensure the remove operation from the coldkey is a success. let tao_staked: u64 = @@ -81,6 +88,10 @@ impl<T: Config> Pallet<T> { /// * 'limit_price' (u64): /// - The limit price expressed in units of RAO per one Alpha. /// + /// * 'allow_partial' (bool): + /// - Allows partial execution of the amount. If set to false, this becomes + /// fill or kill type or order. + /// /// # Event: /// * StakeAdded; /// - On the successfully adding stake to a global account. @@ -104,6 +115,7 @@ impl<T: Config> Pallet<T> { netuid: u16, stake_to_be_added: u64, limit_price: u64, + allow_partial: bool, ) -> dispatch::DispatchResult { // 1. We check that the transaction is signed by the caller and retrieve the T::AccountId coldkey information. let coldkey = ensure_signed(origin)?; @@ -115,16 +127,23 @@ impl<T: Config> Pallet<T> { stake_to_be_added ); - // 2. Validate user input - Self::validate_add_stake(&coldkey, &hotkey, netuid, stake_to_be_added)?; - - // 3. Calcaulate the maximum amount that can be executed with price limit + // 2. Calcaulate the maximum amount that can be executed with price limit let max_amount = Self::get_max_amount_add(netuid, limit_price); let mut possible_stake = stake_to_be_added; if possible_stake > max_amount { possible_stake = max_amount; } + // 3. Validate user input + Self::validate_add_stake( + &coldkey, + &hotkey, + netuid, + stake_to_be_added, + max_amount, + allow_partial, + )?; + // 4. Ensure the remove operation from the coldkey is a success. let tao_staked: u64 = Self::remove_balance_from_coldkey_account(&coldkey, possible_stake)?; diff --git a/pallets/subtensor/src/staking/remove_stake.rs b/pallets/subtensor/src/staking/remove_stake.rs index 564a62f78..743188057 100644 --- a/pallets/subtensor/src/staking/remove_stake.rs +++ b/pallets/subtensor/src/staking/remove_stake.rs @@ -50,7 +50,14 @@ impl<T: Config> Pallet<T> { ); // 2. Validate the user input - Self::validate_remove_stake(&coldkey, &hotkey, netuid, alpha_unstaked)?; + Self::validate_remove_stake( + &coldkey, + &hotkey, + netuid, + alpha_unstaked, + alpha_unstaked, + false, + )?; // 3. Swap the alpba to tao and update counters for this subnet. let fee = DefaultStakingFee::<T>::get(); @@ -223,12 +230,51 @@ impl<T: Config> Pallet<T> { Ok(()) } + /// ---- The implementation for the extrinsic remove_stake_limit: Removes stake from + /// a hotkey on a subnet with a price limit. + /// + /// In case if slippage occurs and the price shall move beyond the limit + /// price, the staking order may execute only partially or not execute + /// at all. + /// + /// # Args: + /// * 'origin': (<T as frame_system::Config>Origin): + /// - The signature of the caller's coldkey. + /// + /// * 'hotkey' (T::AccountId): + /// - The associated hotkey account. + /// + /// * 'amount_unstaked' (u64): + /// - The amount of stake to be added to the hotkey staking account. + /// + /// * 'limit_price' (u64): + /// - The limit price expressed in units of RAO per one Alpha. + /// + /// * 'allow_partial' (bool): + /// - Allows partial execution of the amount. If set to false, this becomes + /// fill or kill type or order. + /// + /// # Event: + /// * StakeRemoved; + /// - On the successfully removing stake from the hotkey account. + /// + /// # Raises: + /// * 'NotRegistered': + /// - Thrown if the account we are attempting to unstake from is non existent. + /// + /// * 'NonAssociatedColdKey': + /// - Thrown if the coldkey does not own the hotkey we are unstaking from. + /// + /// * 'NotEnoughStakeToWithdraw': + /// - Thrown if there is not enough stake on the hotkey to withdwraw this amount. + /// pub fn do_remove_stake_limit( origin: T::RuntimeOrigin, hotkey: T::AccountId, netuid: u16, alpha_unstaked: u64, limit_price: u64, + allow_partial: bool, ) -> dispatch::DispatchResult { // 1. We check the transaction is signed by the caller and retrieve the T::AccountId coldkey information. let coldkey = ensure_signed(origin)?; @@ -240,16 +286,23 @@ impl<T: Config> Pallet<T> { alpha_unstaked ); - // 2. Validate the user input - Self::validate_remove_stake(&coldkey, &hotkey, netuid, alpha_unstaked)?; - - // 3. Calcaulate the maximum amount that can be executed with price limit + // 2. Calcaulate the maximum amount that can be executed with price limit let max_amount = Self::get_max_amount_remove(netuid, limit_price); let mut possible_alpha = alpha_unstaked; if possible_alpha > max_amount { possible_alpha = max_amount; } + // 3. Validate the user input + Self::validate_remove_stake( + &coldkey, + &hotkey, + netuid, + alpha_unstaked, + max_amount, + allow_partial, + )?; + // 4. Swap the alpba to tao and update counters for this subnet. let fee = DefaultStakingFee::<T>::get(); let tao_unstaked: u64 = diff --git a/pallets/subtensor/src/staking/stake_utils.rs b/pallets/subtensor/src/staking/stake_utils.rs index e247f7f4f..4c446947f 100644 --- a/pallets/subtensor/src/staking/stake_utils.rs +++ b/pallets/subtensor/src/staking/stake_utils.rs @@ -729,6 +729,8 @@ impl<T: Config> Pallet<T> { hotkey: &T::AccountId, netuid: u16, stake_to_be_added: u64, + max_amount: u64, + allow_partial: bool, ) -> Result<(), Error<T>> { // Ensure that the subnet exists. ensure!(Self::if_subnet_exist(netuid), Error::<T>::SubnetNotExists); @@ -739,6 +741,12 @@ impl<T: Config> Pallet<T> { // Ensure that the stake_to_be_added is at least the min_amount ensure!(stake_to_be_added >= min_amount, Error::<T>::AmountTooLow); + // Ensure that if partial execution is not allowed, the amount will not cause + // slippage over desired + if !allow_partial { + ensure!(stake_to_be_added <= max_amount, Error::<T>::SlippageTooHigh); + } + // Ensure the callers coldkey has enough stake to perform the transaction. ensure!( Self::can_remove_balance_from_coldkey_account(coldkey, stake_to_be_added), @@ -767,6 +775,8 @@ impl<T: Config> Pallet<T> { hotkey: &T::AccountId, netuid: u16, alpha_unstaked: u64, + max_amount: u64, + allow_partial: bool, ) -> Result<(), Error<T>> { // Ensure that the subnet exists. ensure!(Self::if_subnet_exist(netuid), Error::<T>::SubnetNotExists); @@ -781,6 +791,12 @@ impl<T: Config> Pallet<T> { return Err(Error::<T>::InsufficientLiquidity); }; + // Ensure that if partial execution is not allowed, the amount will not cause + // slippage over desired + if !allow_partial { + ensure!(alpha_unstaked <= max_amount, Error::<T>::SlippageTooHigh); + } + // Ensure that the hotkey account exists this is only possible through registration. ensure!( Self::hotkey_account_exists(hotkey), diff --git a/pallets/subtensor/src/tests/staking.rs b/pallets/subtensor/src/tests/staking.rs index 51887a17d..a96c91b2b 100644 --- a/pallets/subtensor/src/tests/staking.rs +++ b/pallets/subtensor/src/tests/staking.rs @@ -2620,7 +2620,8 @@ fn test_add_stake_limit_ok() { hotkey_account_id, netuid, amount, - limit_price + limit_price, + true )); // Check if stake has increased only by 50 Alpha @@ -2649,6 +2650,57 @@ fn test_add_stake_limit_ok() { }); } +#[test] +fn test_add_stake_limit_fill_or_kill() { + new_test_ext(1).execute_with(|| { + let hotkey_account_id = U256::from(533453); + let coldkey_account_id = U256::from(55453); + let amount = 300_000_000_000; + + // add network + let netuid: u16 = add_dynamic_network(&hotkey_account_id, &coldkey_account_id); + + // Forse-set alpha in and tao reserve to make price equal 1.5 + let tao_reserve: U96F32 = U96F32::from_num(150_000_000_000_u64); + let alpha_in: U96F32 = U96F32::from_num(100_000_000_000_u64); + SubnetTAO::<Test>::insert(netuid, tao_reserve.to_num::<u64>()); + SubnetAlphaIn::<Test>::insert(netuid, alpha_in.to_num::<u64>()); + let current_price: U96F32 = U96F32::from_num(SubtensorModule::get_alpha_price(netuid)); + assert_eq!(current_price, U96F32::from_num(1.5)); + + // Give it some $$$ in his coldkey balance + SubtensorModule::add_balance_to_coldkey_account(&coldkey_account_id, amount); + + // Setup limit price so that it doesn't peak above 4x of current price + // The amount that can be executed at this price is 150 TAO only + // Alpha produced will be equal to 50 = 100 - 150*100/300 + let limit_price = 6_000_000_000; + + // Add stake with slippage safety and check if it fails + assert_noop!( + SubtensorModule::add_stake_limit( + RuntimeOrigin::signed(coldkey_account_id), + hotkey_account_id, + netuid, + amount, + limit_price, + false + ), + Error::<Test>::SlippageTooHigh + ); + + // Lower the amount and it should succeed now + assert_ok!(SubtensorModule::add_stake_limit( + RuntimeOrigin::signed(coldkey_account_id), + hotkey_account_id, + netuid, + amount / 100, + limit_price, + false + )); + }); +} + #[test] fn test_remove_stake_limit_ok() { new_test_ext(1).execute_with(|| { @@ -2694,7 +2746,8 @@ fn test_remove_stake_limit_ok() { hotkey_account_id, netuid, unstake_amount, - limit_price + limit_price, + true )); // Check if stake has decreased only by @@ -2709,3 +2762,58 @@ fn test_remove_stake_limit_ok() { ); }); } + +#[test] +fn test_remove_stake_limit_fill_or_kill() { + new_test_ext(1).execute_with(|| { + let hotkey_account_id = U256::from(533453); + let coldkey_account_id = U256::from(55453); + let stake_amount = 300_000_000_000; + let unstake_amount = 150_000_000_000; + + // add network + let netuid: u16 = add_dynamic_network(&hotkey_account_id, &coldkey_account_id); + + // Give the neuron some stake to remove + SubtensorModule::increase_stake_for_hotkey_and_coldkey_on_subnet( + &hotkey_account_id, + &coldkey_account_id, + netuid, + stake_amount, + ); + + // Forse-set alpha in and tao reserve to make price equal 1.5 + let tao_reserve: U96F32 = U96F32::from_num(150_000_000_000_u64); + let alpha_in: U96F32 = U96F32::from_num(100_000_000_000_u64); + SubnetTAO::<Test>::insert(netuid, tao_reserve.to_num::<u64>()); + SubnetAlphaIn::<Test>::insert(netuid, alpha_in.to_num::<u64>()); + let current_price: U96F32 = U96F32::from_num(SubtensorModule::get_alpha_price(netuid)); + assert_eq!(current_price, U96F32::from_num(1.5)); + + // Setup limit price so that it doesn't drop by more than 10% from current price + let limit_price = 1_350_000_000; + + // Remove stake with slippage safety - fails + assert_noop!( + SubtensorModule::remove_stake_limit( + RuntimeOrigin::signed(coldkey_account_id), + hotkey_account_id, + netuid, + unstake_amount, + limit_price, + false + ), + Error::<Test>::SlippageTooHigh + ); + + // Lower the amount: Should succeed + assert_ok!(SubtensorModule::remove_stake_limit( + RuntimeOrigin::signed(coldkey_account_id), + hotkey_account_id, + netuid, + unstake_amount / 100, + limit_price, + false + ),); + }); +} From 55203dedca0912df3d45e0cdf5aaf8951c7df4d8 Mon Sep 17 00:00:00 2001 From: Greg Zaitsev <gregz@opentensor.dev> Date: Wed, 29 Jan 2025 11:47:39 -0500 Subject: [PATCH 125/145] Bump spec version to 225 --- runtime/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/runtime/src/lib.rs b/runtime/src/lib.rs index 372c9a981..2a2d2fac4 100644 --- a/runtime/src/lib.rs +++ b/runtime/src/lib.rs @@ -220,7 +220,7 @@ pub const VERSION: RuntimeVersion = RuntimeVersion { // `spec_version`, and `authoring_version` are the same between Wasm and native. // This value is set to 100 to notify Polkadot-JS App (https://polkadot.js.org/apps) to use // the compatible custom types. - spec_version: 224, + spec_version: 225, impl_version: 1, apis: RUNTIME_API_VERSIONS, transaction_version: 1, From 0a2635f31f48ee7a4791597d05f8d44a73e4e913 Mon Sep 17 00:00:00 2001 From: Sam Johnson <sam@durosoft.com> Date: Wed, 29 Jan 2025 12:28:27 -0500 Subject: [PATCH 126/145] Update pallets/subtensor/src/tests/staking.rs Co-authored-by: Cameron Fairchild <cameron@opentensor.ai> --- pallets/subtensor/src/tests/staking.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pallets/subtensor/src/tests/staking.rs b/pallets/subtensor/src/tests/staking.rs index a96c91b2b..6a61008a3 100644 --- a/pallets/subtensor/src/tests/staking.rs +++ b/pallets/subtensor/src/tests/staking.rs @@ -2660,7 +2660,7 @@ fn test_add_stake_limit_fill_or_kill() { // add network let netuid: u16 = add_dynamic_network(&hotkey_account_id, &coldkey_account_id); - // Forse-set alpha in and tao reserve to make price equal 1.5 + // Force-set alpha in and tao reserve to make price equal 1.5 let tao_reserve: U96F32 = U96F32::from_num(150_000_000_000_u64); let alpha_in: U96F32 = U96F32::from_num(100_000_000_000_u64); SubnetTAO::<Test>::insert(netuid, tao_reserve.to_num::<u64>()); From c3822c70c23418555680879bbb8e7101d09e52fa Mon Sep 17 00:00:00 2001 From: Sam Johnson <sam@durosoft.com> Date: Wed, 29 Jan 2025 12:28:34 -0500 Subject: [PATCH 127/145] Update pallets/subtensor/src/staking/remove_stake.rs Co-authored-by: Cameron Fairchild <cameron@opentensor.ai> --- pallets/subtensor/src/staking/remove_stake.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pallets/subtensor/src/staking/remove_stake.rs b/pallets/subtensor/src/staking/remove_stake.rs index 743188057..92d0c2e83 100644 --- a/pallets/subtensor/src/staking/remove_stake.rs +++ b/pallets/subtensor/src/staking/remove_stake.rs @@ -303,7 +303,7 @@ impl<T: Config> Pallet<T> { allow_partial, )?; - // 4. Swap the alpba to tao and update counters for this subnet. + // 4. Swap the alpha to tao and update counters for this subnet. let fee = DefaultStakingFee::<T>::get(); let tao_unstaked: u64 = Self::unstake_from_subnet(&hotkey, &coldkey, netuid, possible_alpha, fee); From f644f267244b4da4b5d8b4ba007f8e99bf881bde Mon Sep 17 00:00:00 2001 From: open-junius <zhou@opentensor.dev> Date: Thu, 30 Jan 2025 10:54:47 +0800 Subject: [PATCH 128/145] refactor code --- runtime/src/precompiles/balance_transfer.rs | 9 +- runtime/src/precompiles/mod.rs | 107 ++++---------------- runtime/src/precompiles/neuron.rs | 20 ++-- runtime/src/precompiles/staking.rs | 27 +++-- runtime/src/precompiles/subnet.rs | 15 ++- 5 files changed, 67 insertions(+), 111 deletions(-) diff --git a/runtime/src/precompiles/balance_transfer.rs b/runtime/src/precompiles/balance_transfer.rs index 25dfcfc9f..6154cf8a5 100644 --- a/runtime/src/precompiles/balance_transfer.rs +++ b/runtime/src/precompiles/balance_transfer.rs @@ -1,11 +1,12 @@ -use frame_system::RawOrigin; use pallet_evm::{ BalanceConverter, ExitError, ExitSucceed, PrecompileHandle, PrecompileOutput, PrecompileResult, }; use sp_runtime::traits::UniqueSaturatedInto; use sp_std::vec; -use crate::precompiles::{get_method_id, get_pubkey, get_slice, try_dispatch_runtime_call}; +use crate::precompiles::{ + contract_to_origin, get_method_id, get_pubkey, get_slice, try_dispatch_runtime_call, +}; use crate::Runtime; pub const BALANCE_TRANSFER_INDEX: u64 = 2048; @@ -47,15 +48,13 @@ impl BalanceTransferPrecompile { } let address_bytes_dst = get_slice(txdata, 4, 36)?; - let (account_id_src, _) = get_pubkey(&CONTRACT_ADDRESS_SS58)?; let (account_id_dst, _) = get_pubkey(address_bytes_dst)?; let call = pallet_balances::Call::<Runtime>::transfer_allow_death { dest: account_id_dst.into(), value: amount_sub.unique_saturated_into(), }; - let origin = RawOrigin::Signed(account_id_src); - try_dispatch_runtime_call(handle, call, origin) + try_dispatch_runtime_call(handle, call, contract_to_origin(&CONTRACT_ADDRESS_SS58)?) } } diff --git a/runtime/src/precompiles/mod.rs b/runtime/src/precompiles/mod.rs index e4009d39d..d820f50ec 100644 --- a/runtime/src/precompiles/mod.rs +++ b/runtime/src/precompiles/mod.rs @@ -6,9 +6,8 @@ use core::marker::PhantomData; use frame_support::dispatch::{GetDispatchInfo, Pays}; use pallet_evm::{ - AddressMapping, BalanceConverter, ExitError, ExitSucceed, GasWeightMapping, - HashedAddressMapping, IsPrecompileResult, Precompile, PrecompileFailure, PrecompileHandle, - PrecompileOutput, PrecompileResult, PrecompileSet, + ExitError, ExitSucceed, GasWeightMapping, IsPrecompileResult, Precompile, PrecompileFailure, + PrecompileHandle, PrecompileOutput, PrecompileResult, PrecompileSet, }; use pallet_evm_precompile_modexp::Modexp; use pallet_evm_precompile_sha3fips::Sha3FIPS256; @@ -20,10 +19,6 @@ use crate::{Runtime, RuntimeCall}; use frame_system::RawOrigin; -use sp_core::crypto::Ss58Codec; -use sp_core::U256; -use sp_runtime::traits::{BlakeTwo256, UniqueSaturatedInto}; - use sp_std::vec; // Include custom precompiles @@ -147,84 +142,6 @@ pub fn get_slice(data: &[u8], from: usize, to: usize) -> Result<&[u8], Precompil } } -/// The function return the token to smart contract -fn transfer_back_to_caller( - smart_contract_address: &str, - account_id: &AccountId32, - amount: U256, -) -> Result<(), PrecompileFailure> { - // this is staking smart contract's(0x0000000000000000000000000000000000000801) sr25519 address - let smart_contract_account_id = match AccountId32::from_ss58check(smart_contract_address) { - // match AccountId32::from_ss58check("5CwnBK9Ack1mhznmCnwiibCNQc174pYQVktYW3ayRpLm4K2X") { - Ok(addr) => addr, - Err(_) => { - return Err(PrecompileFailure::Error { - exit_status: ExitError::Other("Invalid SS58 address".into()), - }); - } - }; - let amount_sub = - <Runtime as pallet_evm::Config>::BalanceConverter::into_substrate_balance(amount) - .ok_or(ExitError::OutOfFund)?; - - // Create a transfer call from the smart contract to the caller - let transfer_call = - RuntimeCall::Balances(pallet_balances::Call::<Runtime>::transfer_allow_death { - dest: account_id.clone().into(), - value: amount_sub.unique_saturated_into(), - }); - - // Execute the transfer - let transfer_result = - transfer_call.dispatch(RawOrigin::Signed(smart_contract_account_id).into()); - - if let Err(dispatch_error) = transfer_result { - log::error!( - "Transfer back to caller failed. Error: {:?}", - dispatch_error - ); - return Err(PrecompileFailure::Error { - exit_status: ExitError::Other("Transfer back to caller failed".into()), - }); - } - - Ok(()) -} - -fn dispatch( - handle: &mut impl PrecompileHandle, - call: RuntimeCall, - smart_contract_address: &str, -) -> PrecompileResult { - let account_id = - <HashedAddressMapping<BlakeTwo256> as AddressMapping<AccountId32>>::into_account_id( - handle.context().caller, - ); - - // Transfer the amount back to the caller before executing the staking operation - // let caller = handle.context().caller; - let amount = handle.context().apparent_value; - - if !amount.is_zero() { - transfer_back_to_caller(smart_contract_address, &account_id, amount)?; - } - - let result = call.dispatch(RawOrigin::Signed(account_id.clone()).into()); - match &result { - Ok(post_info) => log::info!("Dispatch succeeded. Post info: {:?}", post_info), - Err(dispatch_error) => log::error!("Dispatch failed. Error: {:?}", dispatch_error), - } - match result { - Ok(_) => Ok(PrecompileOutput { - exit_status: ExitSucceed::Returned, - output: vec![], - }), - Err(_) => Err(PrecompileFailure::Error { - exit_status: ExitError::Other("Subtensor call failed".into()), - }), - } -} - pub fn get_pubkey(data: &[u8]) -> Result<(AccountId32, vec::Vec<u8>), PrecompileFailure> { let mut pubkey = [0u8; 32]; pubkey.copy_from_slice(get_slice(data, 0, 32)?); @@ -235,6 +152,26 @@ pub fn get_pubkey(data: &[u8]) -> Result<(AccountId32, vec::Vec<u8>), Precompile .map_or_else(vec::Vec::new, |slice| slice.to_vec()), )) } + +fn parse_netuid(data: &[u8], offset: usize) -> Result<u16, PrecompileFailure> { + if data.len() < offset + 2 { + return Err(PrecompileFailure::Error { + exit_status: ExitError::InvalidRange, + }); + } + + let mut netuid_bytes = [0u8; 2]; + netuid_bytes.copy_from_slice(get_slice(data, offset, offset + 2)?); + let netuid: u16 = netuid_bytes[1] as u16 | ((netuid_bytes[0] as u16) << 8u16); + + Ok(netuid) +} + +fn contract_to_origin(contract: &[u8; 32]) -> Result<RawOrigin<AccountId32>, PrecompileFailure> { + let (account_id, _) = get_pubkey(contract)?; + Ok(RawOrigin::Signed(account_id)) +} + /// Dispatches a runtime call, but also checks and records the gas costs. fn try_dispatch_runtime_call( handle: &mut impl PrecompileHandle, diff --git a/runtime/src/precompiles/neuron.rs b/runtime/src/precompiles/neuron.rs index efa0b5fec..97167c900 100644 --- a/runtime/src/precompiles/neuron.rs +++ b/runtime/src/precompiles/neuron.rs @@ -1,15 +1,21 @@ use pallet_evm::{ExitError, PrecompileFailure, PrecompileHandle, PrecompileResult}; -use crate::precompiles::{dispatch, get_method_id, get_pubkey, get_slice}; +use crate::precompiles::{ + contract_to_origin, get_method_id, get_pubkey, get_slice, parse_netuid, + try_dispatch_runtime_call, +}; use sp_runtime::AccountId32; use sp_std::vec; use crate::{Runtime, RuntimeCall}; pub const NEURON_PRECOMPILE_INDEX: u64 = 2052; -// this is neuron smart contract's(0x0000000000000000000000000000000000000804) sr25519 address -pub const NEURON_CONTRACT_ADDRESS: &str = "5GKZiUUgTnWSz3BgiVBMehEKkLszsG4ZXnvgWpWFUFKqrqyn"; - +// ss58 public key i.e., the contract sends funds it received to the destination address from the +// method parameter. +const CONTRACT_ADDRESS_SS58: [u8; 32] = [ + 0xbc, 0x46, 0x35, 0x79, 0xbc, 0x99, 0xf9, 0xee, 0x7c, 0x59, 0xed, 0xee, 0x20, 0x61, 0xa3, 0x09, + 0xd2, 0x1e, 0x68, 0xd5, 0x39, 0xb6, 0x40, 0xec, 0x66, 0x46, 0x90, 0x30, 0xab, 0x74, 0xc1, 0xdb, +]; pub struct NeuronPrecompile; impl NeuronPrecompile { @@ -38,7 +44,7 @@ impl NeuronPrecompile { netuid, hotkey, }); - dispatch(handle, call, NEURON_CONTRACT_ADDRESS) + try_dispatch_runtime_call(handle, call, contract_to_origin(&CONTRACT_ADDRESS_SS58)?) } fn parse_netuid_hotkey_parameter(data: &[u8]) -> Result<(u16, AccountId32), PrecompileFailure> { @@ -47,9 +53,7 @@ impl NeuronPrecompile { exit_status: ExitError::InvalidRange, }); } - let mut netuid_vec = [0u8; 2]; - netuid_vec.copy_from_slice(get_slice(data, 30, 32)?); - let netuid = u16::from_be_bytes(netuid_vec); + let netuid = parse_netuid(data, 30)?; let (hotkey, _) = get_pubkey(get_slice(data, 32, 64)?)?; diff --git a/runtime/src/precompiles/staking.rs b/runtime/src/precompiles/staking.rs index d588799fc..3c3ee7ad9 100644 --- a/runtime/src/precompiles/staking.rs +++ b/runtime/src/precompiles/staking.rs @@ -25,7 +25,10 @@ // - Precompile checks the result of do_remove_stake and, in case of a failure, reverts the transaction. // -use crate::precompiles::{dispatch, get_method_id, get_pubkey, get_slice}; +use crate::precompiles::{ + contract_to_origin, get_method_id, get_pubkey, get_slice, parse_netuid, + try_dispatch_runtime_call, +}; use crate::{ProxyType, Runtime, RuntimeCall}; use pallet_evm::{ BalanceConverter, ExitError, ExitSucceed, PrecompileFailure, PrecompileHandle, @@ -36,8 +39,14 @@ use sp_runtime::traits::{StaticLookup, UniqueSaturatedInto}; use sp_std::vec; pub const STAKING_PRECOMPILE_INDEX: u64 = 2049; -// this is staking smart contract's(0x0000000000000000000000000000000000000801) sr25519 address -pub const STAKING_CONTRACT_ADDRESS: &str = "5CwnBK9Ack1mhznmCnwiibCNQc174pYQVktYW3ayRpLm4K2X"; + +// ss58 public key i.e., the contract sends funds it received to the destination address from the +// method parameter. +const CONTRACT_ADDRESS_SS58: [u8; 32] = [ + 0x26, 0xf4, 0x10, 0x1e, 0x52, 0xb7, 0x57, 0x34, 0x33, 0x24, 0x5b, 0xc3, 0x0a, 0xe1, 0x8b, 0x63, + 0x99, 0x53, 0xd8, 0x41, 0x79, 0x33, 0x03, 0x61, 0x4d, 0xfa, 0xcf, 0xf0, 0x37, 0xf7, 0x12, 0x94, +]; + pub struct StakingPrecompile; impl StakingPrecompile { @@ -74,14 +83,16 @@ impl StakingPrecompile { <Runtime as pallet_evm::Config>::BalanceConverter::into_substrate_balance(amount) .ok_or(ExitError::OutOfFund)?; + // let (account_id_src, _) = get_pubkey(&CONTRACT_ADDRESS_SS58)?; // Create the add_stake call let call = RuntimeCall::SubtensorModule(pallet_subtensor::Call::<Runtime>::add_stake { hotkey, netuid, amount_staked: amount_sub.unique_saturated_into(), }); + // let origin = RawOrigin::Signed(account_id_src); // Dispatch the add_stake call - dispatch(handle, call, STAKING_CONTRACT_ADDRESS) + try_dispatch_runtime_call(handle, call, contract_to_origin(&CONTRACT_ADDRESS_SS58)?) } fn remove_stake(handle: &mut impl PrecompileHandle, data: &[u8]) -> PrecompileResult { @@ -104,7 +115,7 @@ impl StakingPrecompile { netuid, amount_unstaked: amount_sub.unique_saturated_into(), }); - dispatch(handle, call, STAKING_CONTRACT_ADDRESS) + try_dispatch_runtime_call(handle, call, contract_to_origin(&CONTRACT_ADDRESS_SS58)?) } fn add_proxy(handle: &mut impl PrecompileHandle, data: &[u8]) -> PrecompileResult { @@ -116,7 +127,7 @@ impl StakingPrecompile { delay: 0, }); - dispatch(handle, call, STAKING_CONTRACT_ADDRESS) + try_dispatch_runtime_call(handle, call, contract_to_origin(&CONTRACT_ADDRESS_SS58)?) } fn remove_proxy(handle: &mut impl PrecompileHandle, data: &[u8]) -> PrecompileResult { @@ -128,13 +139,13 @@ impl StakingPrecompile { delay: 0, }); - dispatch(handle, call, STAKING_CONTRACT_ADDRESS) + try_dispatch_runtime_call(handle, call, contract_to_origin(&CONTRACT_ADDRESS_SS58)?) } fn get_stake(data: &[u8]) -> PrecompileResult { let (hotkey, left_data) = get_pubkey(data)?; let (coldkey, _) = get_pubkey(&left_data)?; - let netuid = Self::parse_netuid(data, 0x5E)?; + let netuid = parse_netuid(data, 0x5E)?; let stake = pallet_subtensor::Pallet::<Runtime>::get_stake_for_hotkey_and_coldkey_on_subnet( &hotkey, &coldkey, netuid, diff --git a/runtime/src/precompiles/subnet.rs b/runtime/src/precompiles/subnet.rs index 3dba5a262..edef73ff0 100644 --- a/runtime/src/precompiles/subnet.rs +++ b/runtime/src/precompiles/subnet.rs @@ -1,4 +1,6 @@ -use crate::precompiles::{dispatch, get_method_id, get_pubkey, get_slice}; +use crate::precompiles::{ + contract_to_origin, get_method_id, get_pubkey, get_slice, try_dispatch_runtime_call, +}; use crate::{Runtime, RuntimeCall}; use pallet_evm::{ExitError, PrecompileFailure, PrecompileHandle, PrecompileResult}; use sp_runtime::AccountId32; @@ -8,9 +10,12 @@ pub const SUBNET_PRECOMPILE_INDEX: u64 = 2051; // three bytes with max lenght 1K pub const MAX_PARAMETER_SIZE: usize = 3 * 1024; -// this is staking smart contract's(0x0000000000000000000000000000000000000803) sr25519 address -pub const STAKING_CONTRACT_ADDRESS: &str = "5DPSUCb5mZFfizvBDSnRoAqmxV5Bmov2CS3xV773qU6VP1w2"; - +// ss58 public key i.e., the contract sends funds it received to the destination address from the +// method parameter. +const CONTRACT_ADDRESS_SS58: [u8; 32] = [ + 0x3a, 0x86, 0x18, 0xfb, 0xbb, 0x1b, 0xbc, 0x47, 0x86, 0x64, 0xff, 0x53, 0x46, 0x18, 0x0c, 0x35, + 0xd0, 0x9f, 0xac, 0x26, 0xf2, 0x02, 0x70, 0x85, 0xb3, 0x1c, 0x56, 0xc1, 0x06, 0x3c, 0x1c, 0xd3, +]; pub struct SubnetPrecompile; impl SubnetPrecompile { @@ -79,7 +84,7 @@ impl SubnetPrecompile { }; // Dispatch the register_network call - dispatch(handle, call, STAKING_CONTRACT_ADDRESS) + try_dispatch_runtime_call(handle, call, contract_to_origin(&CONTRACT_ADDRESS_SS58)?) } fn parse_register_network_parameters( From 40bc8d9c7fb018f949c1636bf818264437394cf6 Mon Sep 17 00:00:00 2001 From: Sam Johnson <sam@durosoft.com> Date: Thu, 30 Jan 2025 02:09:40 -0500 Subject: [PATCH 129/145] fix cargo audit working in CI :tada: --- .github/workflows/cargo-audit.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/cargo-audit.yml b/.github/workflows/cargo-audit.yml index 1934dcaeb..d7904712d 100644 --- a/.github/workflows/cargo-audit.yml +++ b/.github/workflows/cargo-audit.yml @@ -5,6 +5,7 @@ on: - labeled - unlabeled - synchronize + - opened concurrency: group: cargo-audit-${{ github.ref }} cancel-in-progress: true @@ -24,7 +25,7 @@ jobs: sudo apt-get install -y clang curl libssl-dev llvm libudev-dev protobuf-compiler - name: Install cargo-audit - run: cargo install --version 0.20.1 --force cargo-audit + run: cargo install --force cargo-audit - name: Display cargo-audit --version run: cargo audit --version From df596e6cb9c6e3a75d4efde4b66482e2ed5b138f Mon Sep 17 00:00:00 2001 From: Sam Johnson <sam@durosoft.com> Date: Thu, 30 Jan 2025 02:11:43 -0500 Subject: [PATCH 130/145] bump CI From 6f825cc3e5a3454d0b9ed2d34c928d93c9145429 Mon Sep 17 00:00:00 2001 From: Sam Johnson <sam@durosoft.com> Date: Thu, 30 Jan 2025 02:26:54 -0500 Subject: [PATCH 131/145] upgrade futures-util --- Cargo.lock | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 9a828e53f..5709f7c3c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3131,9 +3131,9 @@ dependencies = [ [[package]] name = "futures-channel" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eac8f7d7865dcb88bd4373ab671c8cf4508703796caa2b1985a9ca867b3fcb78" +checksum = "2dff15bf788c671c1934e366d07e30c1814a8ef514e1af724a602e8a2fbe1b10" dependencies = [ "futures-core", "futures-sink", @@ -3141,9 +3141,9 @@ dependencies = [ [[package]] name = "futures-core" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dfc6580bb841c5a68e9ef15c77ccc837b40a7504914d52e47b8b0e9bbda25a1d" +checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e" [[package]] name = "futures-executor" @@ -3170,9 +3170,9 @@ dependencies = [ [[package]] name = "futures-io" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a44623e20b9681a318efdd71c299b6b222ed6f231972bfe2f224ebad6311f0c1" +checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6" [[package]] name = "futures-lite" @@ -3186,9 +3186,9 @@ dependencies = [ [[package]] name = "futures-macro" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" +checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" dependencies = [ "proc-macro2", "quote", @@ -3207,15 +3207,15 @@ dependencies = [ [[package]] name = "futures-sink" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9fb8e00e87438d937621c1c6269e53f536c14d3fbd6a042bb24879e57d474fb5" +checksum = "e575fab7d1e0dcb8d0c7bcf9a63ee213816ab51902e6d244a95819acacf1d4f7" [[package]] name = "futures-task" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38d84fa142264698cdce1a9f9172cf383a0c82de1bddcf3092901442c4097004" +checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988" [[package]] name = "futures-timer" @@ -3225,9 +3225,9 @@ checksum = "f288b0a4f20f9a56b5d1da57e2227c661b7b16168e2f72365f57b63326e29b24" [[package]] name = "futures-util" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d6401deb83407ab3da39eba7e33987a73c3df0c82b4bb5813ee871c19c41d48" +checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81" dependencies = [ "futures-channel", "futures-core", From e876609cb546f8bb01104c4c514dbb02eb8e96a6 Mon Sep 17 00:00:00 2001 From: Sam Johnson <sam@durosoft.com> Date: Thu, 30 Jan 2025 02:35:20 -0500 Subject: [PATCH 132/145] ignore some unmaintained advisories --- .github/workflows/cargo-audit.yml | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/.github/workflows/cargo-audit.yml b/.github/workflows/cargo-audit.yml index d7904712d..e91c4a20f 100644 --- a/.github/workflows/cargo-audit.yml +++ b/.github/workflows/cargo-audit.yml @@ -31,4 +31,11 @@ jobs: run: cargo audit --version - name: cargo audit - run: cargo audit --ignore RUSTSEC-2024-0336 # rustls issue; wait for upstream to resolve this + run: | + cargo audit --ignore RUSTSEC-2024-0336 \ + --ignore RUSTSEC-2021-0127 \ + --ignore RUSTSEC-2024-0370 \ + --ignore RUSTSEC-2022-0080 \ + --ignore RUSTSEC-2022-0061 \ + --ignore RUSTSEC-2020-0168 \ + --ignore RUSTSEC-2024-0384 From 1690ce20295c2b7e44ae857b0247eeca64f9e351 Mon Sep 17 00:00:00 2001 From: Sam Johnson <sam@durosoft.com> Date: Thu, 30 Jan 2025 02:36:03 -0500 Subject: [PATCH 133/145] add another unmaintained --- .github/workflows/cargo-audit.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/cargo-audit.yml b/.github/workflows/cargo-audit.yml index e91c4a20f..69521c475 100644 --- a/.github/workflows/cargo-audit.yml +++ b/.github/workflows/cargo-audit.yml @@ -38,4 +38,5 @@ jobs: --ignore RUSTSEC-2022-0080 \ --ignore RUSTSEC-2022-0061 \ --ignore RUSTSEC-2020-0168 \ - --ignore RUSTSEC-2024-0384 + --ignore RUSTSEC-2024-0384 \ + --ignore RUSTSEC-2024-0388 From 4e7cd7fe00342e25c64c7da62bd604053f56d43b Mon Sep 17 00:00:00 2001 From: Sam Johnson <sam@durosoft.com> Date: Thu, 30 Jan 2025 02:37:06 -0500 Subject: [PATCH 134/145] cargo update url --- Cargo.lock | 250 +++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 243 insertions(+), 7 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 5709f7c3c..010e99494 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3782,6 +3782,124 @@ dependencies = [ "cc", ] +[[package]] +name = "icu_collections" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db2fa452206ebee18c4b5c2274dbf1de17008e874b4dc4f0aea9d01ca79e4526" +dependencies = [ + "displaydoc", + "yoke", + "zerofrom", + "zerovec", +] + +[[package]] +name = "icu_locid" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13acbb8371917fc971be86fc8057c41a64b521c184808a698c02acc242dbf637" +dependencies = [ + "displaydoc", + "litemap", + "tinystr", + "writeable", + "zerovec", +] + +[[package]] +name = "icu_locid_transform" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "01d11ac35de8e40fdeda00d9e1e9d92525f3f9d887cdd7aa81d727596788b54e" +dependencies = [ + "displaydoc", + "icu_locid", + "icu_locid_transform_data", + "icu_provider", + "tinystr", + "zerovec", +] + +[[package]] +name = "icu_locid_transform_data" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fdc8ff3388f852bede6b579ad4e978ab004f139284d7b28715f773507b946f6e" + +[[package]] +name = "icu_normalizer" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19ce3e0da2ec68599d193c93d088142efd7f9c5d6fc9b803774855747dc6a84f" +dependencies = [ + "displaydoc", + "icu_collections", + "icu_normalizer_data", + "icu_properties", + "icu_provider", + "smallvec", + "utf16_iter", + "utf8_iter", + "write16", + "zerovec", +] + +[[package]] +name = "icu_normalizer_data" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8cafbf7aa791e9b22bec55a167906f9e1215fd475cd22adfcf660e03e989516" + +[[package]] +name = "icu_properties" +version = "1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93d6020766cfc6302c15dbbc9c8778c37e62c14427cb7f6e601d849e092aeef5" +dependencies = [ + "displaydoc", + "icu_collections", + "icu_locid_transform", + "icu_properties_data", + "icu_provider", + "tinystr", + "zerovec", +] + +[[package]] +name = "icu_properties_data" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67a8effbc3dd3e4ba1afa8ad918d5684b8868b3b26500753effea8d2eed19569" + +[[package]] +name = "icu_provider" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ed421c8a8ef78d3e2dbc98a973be2f3770cb42b606e3ab18d6237c4dfde68d9" +dependencies = [ + "displaydoc", + "icu_locid", + "icu_provider_macros", + "stable_deref_trait", + "tinystr", + "writeable", + "yoke", + "zerofrom", + "zerovec", +] + +[[package]] +name = "icu_provider_macros" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ec89e9337638ecdc08744df490b221a7399bf8d164eb52a665454e60e075ad6" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.90", +] + [[package]] name = "ident_case" version = "1.0.1" @@ -3811,12 +3929,23 @@ dependencies = [ [[package]] name = "idna" -version = "0.5.0" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "634d9b1461af396cad843f47fdba5597a4f9e6ddd4bfb6ff5d85028c25cb12f6" +checksum = "686f825264d630750a544639377bae737628043f20d38bbc029e8f29ea968a7e" dependencies = [ - "unicode-bidi", - "unicode-normalization", + "idna_adapter", + "smallvec", + "utf8_iter", +] + +[[package]] +name = "idna_adapter" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "daca1df1c957320b2cf139ac61e7bd64fed304c5040df000a745aa1de3b4ef71" +dependencies = [ + "icu_normalizer", + "icu_properties", ] [[package]] @@ -4847,6 +4976,12 @@ dependencies = [ "keystream", ] +[[package]] +name = "litemap" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ee93343901ab17bd981295f2cf0026d4ad018c7c31ba84549a4ddbb47a45104" + [[package]] name = "litep2p" version = "0.6.2" @@ -11176,6 +11311,16 @@ dependencies = [ "crunchy", ] +[[package]] +name = "tinystr" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9117f5d4db391c1cf6927e7bea3db74b9a1c1add8f7eda9ffd5364f40f57b82f" +dependencies = [ + "displaydoc", + "zerovec", +] + [[package]] name = "tinyvec" version = "1.8.0" @@ -11723,12 +11868,12 @@ checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" [[package]] name = "url" -version = "2.5.2" +version = "2.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22784dbdf76fdde8af1aeda5622b546b422b6fc585325248a2bf9f5e41e94d6c" +checksum = "32f8b686cadd1473f4bd0117a5d28d36b1ade384ea9b5069a1c40aefed7fda60" dependencies = [ "form_urlencoded", - "idna 0.5.0", + "idna 1.0.3", "percent-encoding", ] @@ -11738,6 +11883,18 @@ version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9" +[[package]] +name = "utf16_iter" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8232dd3cdaed5356e0f716d285e4b40b932ac434100fe9b7e0e8e935b9e6246" + +[[package]] +name = "utf8_iter" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" + [[package]] name = "utf8parse" version = "0.2.2" @@ -12514,6 +12671,18 @@ dependencies = [ "windows-sys 0.48.0", ] +[[package]] +name = "write16" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d1890f4022759daae28ed4fe62859b1236caebfc61ede2f63ed4e695f3f6d936" + +[[package]] +name = "writeable" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e9df38ee2d2c3c5948ea468a8406ff0db0b29ae1ffde1bcf20ef305bcc95c51" + [[package]] name = "wyz" version = "0.5.1" @@ -12619,6 +12788,30 @@ dependencies = [ "time", ] +[[package]] +name = "yoke" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "120e6aef9aa629e3d4f52dc8cc43a015c7724194c97dfaf45180d2daf2b77f40" +dependencies = [ + "serde", + "stable_deref_trait", + "yoke-derive", + "zerofrom", +] + +[[package]] +name = "yoke-derive" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2380878cad4ac9aac1e2435f3eb4020e8374b5f13c296cb75b4620ff8e229154" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.90", + "synstructure 0.13.1", +] + [[package]] name = "zerocopy" version = "0.7.35" @@ -12640,6 +12833,27 @@ dependencies = [ "syn 2.0.90", ] +[[package]] +name = "zerofrom" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cff3ee08c995dee1859d998dea82f7374f2826091dd9cd47def953cae446cd2e" +dependencies = [ + "zerofrom-derive", +] + +[[package]] +name = "zerofrom-derive" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "595eed982f7d355beb85837f651fa22e90b3c044842dc7f2c2842c086f295808" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.90", + "synstructure 0.13.1", +] + [[package]] name = "zeroize" version = "1.8.1" @@ -12660,6 +12874,28 @@ dependencies = [ "syn 2.0.90", ] +[[package]] +name = "zerovec" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa2b893d79df23bfb12d5461018d408ea19dfafe76c2c7ef6d4eba614f8ff079" +dependencies = [ + "yoke", + "zerofrom", + "zerovec-derive", +] + +[[package]] +name = "zerovec-derive" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6eafa6dfb17584ea3e2bd6e76e0cc15ad7af12b09abdd1ca55961bed9b1063c6" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.90", +] + [[package]] name = "zstd" version = "0.11.2+zstd.1.5.2" From c6d0b443977afc28644fd8b2ebe4a39fd0061cae Mon Sep 17 00:00:00 2001 From: Sam Johnson <sam@durosoft.com> Date: Thu, 30 Jan 2025 02:40:05 -0500 Subject: [PATCH 135/145] fix more advisories --- Cargo.lock | 13 ++----------- 1 file changed, 2 insertions(+), 11 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 010e99494..6156794ba 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4171,15 +4171,6 @@ dependencies = [ "either", ] -[[package]] -name = "itertools" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba291022dbbd398a455acf126c1e341954079855bc60dfdda641363bd6922569" -dependencies = [ - "either", -] - [[package]] name = "itoa" version = "1.0.11" @@ -7400,7 +7391,7 @@ checksum = "22505a5c94da8e3b7c2996394d1c933236c4d743e81a410bcca4e6989fc066a4" dependencies = [ "bytes", "heck 0.5.0", - "itertools 0.12.1", + "itertools 0.11.0", "log", "multimap", "once_cell", @@ -7433,7 +7424,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "81bddcdb20abf9501610992b6759a4c888aef7d1a7247ef75e2404275ac24af1" dependencies = [ "anyhow", - "itertools 0.12.1", + "itertools 0.11.0", "proc-macro2", "quote", "syn 2.0.90", From 4166c5eb4abe322fd649e1ecac54cd7081841ed7 Mon Sep 17 00:00:00 2001 From: Sam Johnson <sam@durosoft.com> Date: Thu, 30 Jan 2025 03:25:20 -0500 Subject: [PATCH 136/145] fix more advisories --- .github/workflows/cargo-audit.yml | 3 +- Cargo.lock | 1626 ++++++++++++++++------------- 2 files changed, 905 insertions(+), 724 deletions(-) diff --git a/.github/workflows/cargo-audit.yml b/.github/workflows/cargo-audit.yml index 69521c475..14e2d9449 100644 --- a/.github/workflows/cargo-audit.yml +++ b/.github/workflows/cargo-audit.yml @@ -39,4 +39,5 @@ jobs: --ignore RUSTSEC-2022-0061 \ --ignore RUSTSEC-2020-0168 \ --ignore RUSTSEC-2024-0384 \ - --ignore RUSTSEC-2024-0388 + --ignore RUSTSEC-2024-0388 \ + --ignore RUSTSEC-2024-0421 diff --git a/Cargo.lock b/Cargo.lock index 6156794ba..42522da32 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -23,11 +23,11 @@ dependencies = [ [[package]] name = "addr2line" -version = "0.24.1" +version = "0.24.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f5fb1d8e4442bd405fdfd1dacb42792696b0cf9cb15882e5d097b742a676d375" +checksum = "dfbe277e56a376000877090da837660b4427aad530e3028d44e0bffe4f89a1c1" dependencies = [ - "gimli 0.31.0", + "gimli 0.31.1", ] [[package]] @@ -77,7 +77,7 @@ version = "0.7.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "891477e0c6a8957309ee5c45a6368af3ae14bb510732d2684ffa19af310920f9" dependencies = [ - "getrandom", + "getrandom 0.2.15", "once_cell", "version_check", ] @@ -89,7 +89,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011" dependencies = [ "cfg-if", - "getrandom", + "getrandom 0.2.15", "once_cell", "version_check", "zerocopy", @@ -106,9 +106,9 @@ dependencies = [ [[package]] name = "allocator-api2" -version = "0.2.18" +version = "0.2.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c6cb57a04249c6480766f7f7cef5467412af1490f8d1e243141daddada3264f" +checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923" [[package]] name = "android-tzdata" @@ -127,9 +127,9 @@ dependencies = [ [[package]] name = "anstream" -version = "0.6.15" +version = "0.6.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64e15c1ab1f89faffbf04a634d5e1962e9074f2741eef6d97f3c4e322426d526" +checksum = "8acc5369981196006228e28809f761875c0327210a891e941f4c683b3a99529b" dependencies = [ "anstyle", "anstyle-parse", @@ -142,43 +142,44 @@ dependencies = [ [[package]] name = "anstyle" -version = "1.0.8" +version = "1.0.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1bec1de6f59aedf83baf9ff929c98f2ad654b97c9510f4e70cf6f661d49fd5b1" +checksum = "55cc3b69f167a1ef2e161439aa98aed94e6028e5f9a59be9a6ffb47aef1651f9" [[package]] name = "anstyle-parse" -version = "0.2.5" +version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb47de1e80c2b463c735db5b217a0ddc39d612e7ac9e2e96a5aed1f57616c1cb" +checksum = "3b2d16507662817a6a20a9ea92df6652ee4f94f914589377d69f3b21bc5798a9" dependencies = [ "utf8parse", ] [[package]] name = "anstyle-query" -version = "1.1.1" +version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d36fc52c7f6c869915e99412912f22093507da8d9e942ceaf66fe4b7c14422a" +checksum = "79947af37f4177cfead1110013d678905c37501914fba0efea834c3fe9a8d60c" dependencies = [ - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] name = "anstyle-wincon" -version = "3.0.4" +version = "3.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5bf74e1b6e971609db8ca7a9ce79fd5768ab6ae46441c572e46cf596f59e57f8" +checksum = "ca3534e77181a9cc07539ad51f2141fe32f6c3ffd4df76db8ad92346b003ae4e" dependencies = [ "anstyle", - "windows-sys 0.52.0", + "once_cell", + "windows-sys 0.59.0", ] [[package]] name = "anyhow" -version = "1.0.89" +version = "1.0.95" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "86fdf8605db99b54d3cd748a44c6d04df638eb5dafb219b135d0149bd0db01f6" +checksum = "34ac096ce696dc2fcabef30516bb13c0a68a11d30131d3df6f04711467681b04" [[package]] name = "approx" @@ -200,7 +201,7 @@ dependencies = [ "proc-macro-error", "proc-macro2", "quote", - "syn 2.0.90", + "syn 2.0.96", ] [[package]] @@ -567,7 +568,7 @@ dependencies = [ "nom", "num-traits", "rusticata-macros", - "thiserror", + "thiserror 1.0.69", "time", ] @@ -583,7 +584,7 @@ dependencies = [ "nom", "num-traits", "rusticata-macros", - "thiserror", + "thiserror 1.0.69", "time", ] @@ -607,7 +608,7 @@ checksum = "965c2d33e53cb6b267e148a4cb0760bc01f4904c1cd4bb4002a085bb016d1490" dependencies = [ "proc-macro2", "quote", - "syn 2.0.90", + "syn 2.0.96", "synstructure 0.13.1", ] @@ -630,7 +631,7 @@ checksum = "7b18050c2cd6fe86c3a76584ef5e0baf286d038cda203eb6223df2cc413565f7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.90", + "syn 2.0.96", ] [[package]] @@ -652,9 +653,9 @@ dependencies = [ [[package]] name = "async-io" -version = "2.3.4" +version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "444b0228950ee6501b3568d3c93bf1176a1fdbc3b758dcd9475046d30f4dc7e8" +checksum = "43a2b323ccce0a1d90b449fd71f2a06ca7faa7c54c2751f06c9bd851fc061059" dependencies = [ "async-lock", "cfg-if", @@ -663,7 +664,7 @@ dependencies = [ "futures-lite", "parking", "polling", - "rustix 0.38.37", + "rustix 0.38.44", "slab", "tracing", "windows-sys 0.59.0", @@ -675,20 +676,20 @@ version = "3.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ff6e472cdea888a4bd64f342f09b3f50e1886d32afe8df3d663c01140b811b18" dependencies = [ - "event-listener 5.3.1", + "event-listener 5.4.0", "event-listener-strategy", "pin-project-lite", ] [[package]] name = "async-trait" -version = "0.1.83" +version = "0.1.85" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "721cae7de5c34fbb2acd27e21e6d2cf7b886dce0c27388d46c4e6c47ea4318dd" +checksum = "3f934833b4b7233644e5848f235df3f57ed8c80f1528a26c3dfa13d2147fa056" dependencies = [ "proc-macro2", "quote", - "syn 2.0.90", + "syn 2.0.96", ] [[package]] @@ -732,13 +733,13 @@ dependencies = [ [[package]] name = "auto_impl" -version = "1.2.0" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c87f3f15e7794432337fc718554eaa4dc8f04c9677a950ffe366f20a162ae42" +checksum = "e12882f59de5360c748c4cbf569a042d5fb0eb515f7bea9c1f470b47f6ffbd73" dependencies = [ "proc-macro2", "quote", - "syn 2.0.90", + "syn 2.0.96", ] [[package]] @@ -753,11 +754,11 @@ version = "0.3.74" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8d82cb332cdfaed17ae235a638438ac4d4839913cc2af585c3c6746e8f8bee1a" dependencies = [ - "addr2line 0.24.1", + "addr2line 0.24.2", "cfg-if", "libc", "miniz_oxide", - "object 0.36.4", + "object 0.36.7", "rustc-demangle", "windows-targets 0.52.6", ] @@ -819,13 +820,13 @@ dependencies = [ "lazy_static", "lazycell", "peeking_take_while", - "prettyplease 0.2.22", + "prettyplease 0.2.29", "proc-macro2", "quote", "regex", "rustc-hash 1.1.0", "shlex", - "syn 2.0.90", + "syn 2.0.96", ] [[package]] @@ -852,9 +853,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitflags" -version = "2.6.0" +version = "2.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" +checksum = "8f68f53c83ab957f72c32642f3868eec03eb974d1fb82e453128456482613d36" [[package]] name = "bitvec" @@ -913,9 +914,9 @@ dependencies = [ [[package]] name = "blake3" -version = "1.5.4" +version = "1.5.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d82033247fd8e890df8f740e407ad4d038debb9eb1f40533fffb32e7d17dc6f7" +checksum = "b8ee0c1824c4dea5b5f81736aff91bae041d2c07ee1192bec91054e10e3e601e" dependencies = [ "arrayref", "arrayvec", @@ -944,9 +945,9 @@ dependencies = [ [[package]] name = "bounded-collections" -version = "0.2.0" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d32385ecb91a31bddaf908e8dcf4a15aef1bcd3913cc03ebfad02ff6d568abc1" +checksum = "3d077619e9c237a5d1875166f5e8033e8f6bff0c96f8caf81e1c2d7738c431bf" dependencies = [ "log", "parity-scale-codec", @@ -980,9 +981,9 @@ dependencies = [ [[package]] name = "bumpalo" -version = "3.16.0" +version = "3.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c" +checksum = "1628fb46dfa0b37568d12e5edd512553eccf6a22a78e8bde00bb4aed84d5bdbf" [[package]] name = "byte-slice-cast" @@ -998,9 +999,9 @@ checksum = "e3b5ca7a04898ad4bcd41c90c5285445ff5b791899bb1b0abdd2a2aa791211d7" [[package]] name = "bytemuck" -version = "1.18.0" +version = "1.21.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94bbb0ad554ad961ddc5da507a12a29b14e4ae5bda06b19f575a3e6079d2e2ae" +checksum = "ef657dfab802224e671f5818e9a4935f9b1957ed18e58292690cc39e7a4092a3" [[package]] name = "byteorder" @@ -1010,9 +1011,9 @@ checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" [[package]] name = "bytes" -version = "1.7.2" +version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "428d9aa8fbc0670b7b8d6030a7fadd0f86151cae55e4dbbece15f3780a3dfaf3" +checksum = "325918d6fe32f23b19878fe4b34794ae41fc19ddbe53b10571a4874d44ffd39b" [[package]] name = "bzip2-sys" @@ -1046,9 +1047,9 @@ dependencies = [ [[package]] name = "cargo-platform" -version = "0.1.8" +version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "24b1f0365a6c6bb4020cd05806fd0d33c44d38046b8bd7f0e40814b9763cabfc" +checksum = "e35af189006b9c0f00a064685c727031e3ed2d8020f7ba284d78cc2671bd36ea" dependencies = [ "serde", ] @@ -1061,17 +1062,17 @@ checksum = "eee4243f1f26fc7a42710e7439c149e2b10b05472f88090acce52632f231a73a" dependencies = [ "camino", "cargo-platform", - "semver 1.0.23", + "semver 1.0.25", "serde", "serde_json", - "thiserror", + "thiserror 1.0.69", ] [[package]] name = "cc" -version = "1.1.24" +version = "1.2.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "812acba72f0a070b003d3697490d2b55b837230ae7c6c6497f05cc2ddbb8d938" +checksum = "13208fcbb66eaeffe09b99fffbe1af420f00a7b35aa99ad683dfc1aa76145229" dependencies = [ "jobserver", "libc", @@ -1144,9 +1145,9 @@ dependencies = [ [[package]] name = "chrono" -version = "0.4.38" +version = "0.4.39" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a21f936df1771bf62b77f047b726c4625ff2e8aa607c01ec06e5a05bd8463401" +checksum = "7e36cc9d416881d2e24f9a963be5fb1cd90966419ac844274161d10488b3e825" dependencies = [ "android-tzdata", "iana-time-zone", @@ -1216,9 +1217,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.5.19" +version = "4.5.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7be5744db7978a28d9df86a214130d106a89ce49644cbc4e3f0c22c3fba30615" +checksum = "769b0145982b4b48713e01ec42d61614425f27b7058bda7180a3a41f30104796" dependencies = [ "clap_builder", "clap_derive", @@ -1226,9 +1227,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.5.19" +version = "4.5.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a5fbc17d3ef8278f55b282b2a2e75ae6f6c7d4bb70ed3d0382375104bfafdb4b" +checksum = "1b26884eb4b57140e4d2d93652abfa49498b938b3c9179f9fc487b0acc3edad7" dependencies = [ "anstream", "anstyle", @@ -1239,21 +1240,21 @@ dependencies = [ [[package]] name = "clap_derive" -version = "4.5.18" +version = "4.5.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ac6a0c7b1a9e9a5186361f67dfa1b88213572f427fb9ab038efb2bd8c582dab" +checksum = "54b755194d6389280185988721fffba69495eed5ee9feeee9a599b53db80318c" dependencies = [ "heck 0.5.0", "proc-macro2", "quote", - "syn 2.0.90", + "syn 2.0.96", ] [[package]] name = "clap_lex" -version = "0.7.2" +version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1462739cb27611015575c0c11df5df7601141071f07518d56fcc1be504cbec97" +checksum = "f46ad14479a25103f283c0f10005961cf086d8dc42205bb44c46ac563475dca6" [[package]] name = "codespan-reporting" @@ -1262,14 +1263,14 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3538270d33cc669650c4b093848450d380def10c331d38c768e34cac80576e6e" dependencies = [ "termcolor", - "unicode-width", + "unicode-width 0.1.14", ] [[package]] name = "colorchoice" -version = "1.0.2" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3fd119d74b830634cea2a0f58bbd0d54540518a14397557951e79340abc28c0" +checksum = "5b63caa9aa9397e2d9480a9b13673856c78d8ac123288526c37d7839f2a86990" [[package]] name = "combine" @@ -1283,13 +1284,13 @@ dependencies = [ [[package]] name = "comfy-table" -version = "7.1.1" +version = "7.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b34115915337defe99b2aff5c2ce6771e5fbc4079f4b506301f5cf394c8452f7" +checksum = "24f165e7b643266ea80cb858aed492ad9280e3e05ce24d4a99d7d7b889b6a4d9" dependencies = [ "strum 0.26.3", "strum_macros 0.26.4", - "unicode-width", + "unicode-width 0.2.0", ] [[package]] @@ -1309,15 +1310,15 @@ dependencies = [ [[package]] name = "console" -version = "0.15.8" +version = "0.15.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e1f83fc076bd6dd27517eacdf25fef6c4dfe5f1d7448bafaaf3a26f13b5e4eb" +checksum = "ea3c6ecd8059b57859df5c69830340ed3c41d30e3da0c1cbed90a96ac853041b" dependencies = [ "encode_unicode", - "lazy_static", "libc", - "unicode-width", - "windows-sys 0.52.0", + "once_cell", + "unicode-width 0.2.0", + "windows-sys 0.59.0", ] [[package]] @@ -1341,7 +1342,7 @@ version = "0.1.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f9d839f2a20b0aee515dc581a6172f2321f96cab76c1a38a4c584a194955390e" dependencies = [ - "getrandom", + "getrandom 0.2.15", "once_cell", "tiny-keccak", ] @@ -1400,9 +1401,9 @@ dependencies = [ [[package]] name = "cpufeatures" -version = "0.2.14" +version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "608697df725056feaccfa42cffdaeeec3fccc4ffc38358ecd19b243e716a78e0" +checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280" dependencies = [ "libc", ] @@ -1531,9 +1532,9 @@ dependencies = [ [[package]] name = "crossbeam-deque" -version = "0.8.5" +version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "613f8cc01fe9cf1a3eb3d7f488fd2fa8388403e97039e2f73692932e291a770d" +checksum = "9dd111b7b7f7d55b72c0a6ae361660ee5853c9af73f70c3c2ef6858b950e2e51" dependencies = [ "crossbeam-epoch", "crossbeam-utils", @@ -1559,15 +1560,15 @@ dependencies = [ [[package]] name = "crossbeam-utils" -version = "0.8.20" +version = "0.8.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22ec99545bb0ed0ea7bb9b8e1e9122ea386ff8a48c0922e43f36d45ab09e0e80" +checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" [[package]] name = "crunchy" -version = "0.2.2" +version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" +checksum = "43da5946c66ffcc7745f48db692ffbb10a83bfe0afd96235c5c2a4fb23994929" [[package]] name = "crypto-bigint" @@ -1645,51 +1646,66 @@ checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3" dependencies = [ "proc-macro2", "quote", - "syn 2.0.90", + "syn 2.0.96", ] [[package]] name = "cxx" -version = "1.0.128" +version = "1.0.137" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "54ccead7d199d584d139148b04b4a368d1ec7556a1d9ea2548febb1b9d49f9a4" +checksum = "0fc894913dccfed0f84106062c284fa021c3ba70cb1d78797d6f5165d4492e45" dependencies = [ "cc", + "cxxbridge-cmd", "cxxbridge-flags", "cxxbridge-macro", + "foldhash", "link-cplusplus", ] [[package]] name = "cxx-build" -version = "1.0.128" +version = "1.0.137" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c77953e99f01508f89f55c494bfa867171ef3a6c8cea03d26975368f2121a5c1" +checksum = "503b2bfb6b3e8ce7f95d865a67419451832083d3186958290cee6c53e39dfcfe" dependencies = [ "cc", "codespan-reporting", - "once_cell", "proc-macro2", "quote", "scratch", - "syn 2.0.90", + "syn 2.0.96", +] + +[[package]] +name = "cxxbridge-cmd" +version = "1.0.137" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e0d2cb64a95b4b5a381971482235c4db2e0208302a962acdbe314db03cbbe2fb" +dependencies = [ + "clap", + "codespan-reporting", + "proc-macro2", + "quote", + "syn 2.0.96", ] [[package]] name = "cxxbridge-flags" -version = "1.0.128" +version = "1.0.137" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "65777e06cc48f0cb0152024c77d6cf9e4bdb4408e7b48bea993d42fa0f5b02b6" +checksum = "5f797b0206463c9c2a68ed605ab28892cca784f1ef066050f4942e3de26ad885" [[package]] name = "cxxbridge-macro" -version = "1.0.128" +version = "1.0.137" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "98532a60dedaebc4848cb2cba5023337cc9ea3af16a5b062633fabfd9f18fb60" +checksum = "e79010a2093848e65a3e0f7062d3f02fb2ef27f866416dfe436fccfa73d3bb59" dependencies = [ "proc-macro2", "quote", - "syn 2.0.90", + "rustversion", + "syn 2.0.96", ] [[package]] @@ -1713,7 +1729,7 @@ dependencies = [ "proc-macro2", "quote", "strsim", - "syn 2.0.90", + "syn 2.0.96", ] [[package]] @@ -1724,7 +1740,7 @@ checksum = "d336a2a514f6ccccaa3e09b02d41d35330c07ddf03a62165fcec10bb561c7806" dependencies = [ "darling_core", "quote", - "syn 2.0.90", + "syn 2.0.96", ] [[package]] @@ -1742,15 +1758,15 @@ dependencies = [ [[package]] name = "data-encoding" -version = "2.6.0" +version = "2.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8566979429cf69b49a5c740c60791108e86440e8be149bbea4fe54d2c32d6e2" +checksum = "0e60eed09d8c01d3cee5b7d30acb059b76614c918fa0f992e0dd6eeb10daad6f" [[package]] name = "data-encoding-macro" -version = "0.1.15" +version = "0.1.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1559b6cba622276d6d63706db152618eeb15b89b3e4041446b05876e352e639" +checksum = "5b16d9d0d88a5273d830dac8b78ceb217ffc9b1d5404e5597a3542515329405b" dependencies = [ "data-encoding", "data-encoding-macro-internal", @@ -1758,12 +1774,12 @@ dependencies = [ [[package]] name = "data-encoding-macro-internal" -version = "0.1.13" +version = "0.1.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "332d754c0af53bc87c108fed664d121ecf59207ec4196041f04d6ab9002ad33f" +checksum = "1145d32e826a7748b69ee8fc62d3e6355ff7f1051df53141e7048162fc90481b" dependencies = [ "data-encoding", - "syn 1.0.109", + "syn 2.0.96", ] [[package]] @@ -1833,7 +1849,7 @@ checksum = "d65d7ce8132b7c0e54497a4d9a55a1c2a0912a0d786cf894472ba818fba45762" dependencies = [ "proc-macro2", "quote", - "syn 2.0.90", + "syn 2.0.96", ] [[package]] @@ -1846,7 +1862,27 @@ dependencies = [ "proc-macro2", "quote", "rustc_version 0.4.1", - "syn 2.0.90", + "syn 2.0.96", +] + +[[package]] +name = "derive_more" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a9b99b9cbbe49445b21764dc0625032a89b145a2642e67603e1c936f5458d05" +dependencies = [ + "derive_more-impl", +] + +[[package]] +name = "derive_more-impl" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb7330aeadfbe296029522e6c40f315320aba36fc43a5b3632f3795348f3bd22" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.96", ] [[package]] @@ -1935,23 +1971,23 @@ checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.90", + "syn 2.0.96", ] [[package]] name = "docify" -version = "0.2.8" +version = "0.2.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43a2f138ad521dc4a2ced1a4576148a6a610b4c5923933b062a263130a6802ce" +checksum = "a772b62b1837c8f060432ddcc10b17aae1453ef17617a99bc07789252d2a5896" dependencies = [ "docify_macros", ] [[package]] name = "docify_macros" -version = "0.2.8" +version = "0.2.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a081e51fb188742f5a7a1164ad752121abcb22874b21e2c3b0dd040c515fdad" +checksum = "60e6be249b0a462a14784a99b19bf35a667bb5e09de611738bb7362fa4c95ff7" dependencies = [ "common-path", "derive-syn-parse", @@ -1959,7 +1995,7 @@ dependencies = [ "proc-macro2", "quote", "regex", - "syn 2.0.90", + "syn 2.0.96", "termcolor", "toml 0.8.19", "walkdir", @@ -1985,9 +2021,9 @@ checksum = "dcbb2bf8e87535c23f7a8a321e364ce21462d0ff10cb6407820e8e96dfff6653" [[package]] name = "dyn-clonable" -version = "0.9.0" +version = "0.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e9232f0e607a262ceb9bd5141a3dfb3e4db6994b31989bbfd845878cba59fd4" +checksum = "a36efbb9bfd58e1723780aa04b61aba95ace6a05d9ffabfdb0b43672552f0805" dependencies = [ "dyn-clonable-impl", "dyn-clone", @@ -1995,13 +2031,13 @@ dependencies = [ [[package]] name = "dyn-clonable-impl" -version = "0.9.0" +version = "0.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "558e40ea573c374cf53507fd240b7ee2f5477df7cfebdb97323ec61c719399c5" +checksum = "7e8671d54058979a37a26f3511fbf8d198ba1aa35ffb202c42587d918d77213a" dependencies = [ "proc-macro2", "quote", - "syn 1.0.109", + "syn 2.0.96", ] [[package]] @@ -2096,9 +2132,9 @@ dependencies = [ [[package]] name = "encode_unicode" -version = "0.3.6" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a357d28ed41a50f9c765dbfe56cbc04a64e53e5fc58ba79fbc34c10ef3df831f" +checksum = "34aa73646ffb006b8f5147f3dc182bd4bcb190227ce861fc4a4844bf8e3cb2c0" [[package]] name = "enum-as-inner" @@ -2121,27 +2157,27 @@ dependencies = [ "heck 0.5.0", "proc-macro2", "quote", - "syn 2.0.90", + "syn 2.0.96", ] [[package]] name = "enumflags2" -version = "0.7.10" +version = "0.7.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d232db7f5956f3f14313dc2f87985c58bd2c695ce124c8cdd984e08e15ac133d" +checksum = "ba2f4b465f5318854c6f8dd686ede6c0a9dc67d4b1ac241cf0eb51521a309147" dependencies = [ "enumflags2_derive", ] [[package]] name = "enumflags2_derive" -version = "0.7.10" +version = "0.7.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "de0d48a183585823424a4ce1aa132d174a6a81bd540895822eb4c8373a8e49e8" +checksum = "fc4caf64a58d7a6d65ab00639b046ff54399a39f5f2554728895ace4b297cd79" dependencies = [ "proc-macro2", "quote", - "syn 2.0.90", + "syn 2.0.96", ] [[package]] @@ -2171,12 +2207,12 @@ checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" [[package]] name = "errno" -version = "0.3.9" +version = "0.3.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "534c5cf6194dfab3db3242765c03bbe257cf92f22b38f6bc0c58d59108a820ba" +checksum = "33d852cb9b869c2a9b3df2f71a3074817f01e1844f839a144f5fcef059a4eb5d" dependencies = [ "libc", - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] @@ -2187,9 +2223,9 @@ checksum = "c22d4b5885b6aa2fe5e8b9329fb8d232bf739e434e6b87347c63bdd00c120f60" dependencies = [ "crunchy", "fixed-hash", - "impl-codec", + "impl-codec 0.6.0", "impl-rlp", - "impl-serde", + "impl-serde 0.4.0", "scale-info", "tiny-keccak", ] @@ -2220,12 +2256,12 @@ checksum = "02d215cbf040552efcbe99a38372fe80ab9d00268e20012b79fcd0f073edd8ee" dependencies = [ "ethbloom", "fixed-hash", - "impl-codec", + "impl-codec 0.6.0", "impl-rlp", - "impl-serde", - "primitive-types", + "impl-serde 0.4.0", + "primitive-types 0.12.2", "scale-info", - "uint", + "uint 0.9.5", ] [[package]] @@ -2236,9 +2272,9 @@ checksum = "0206175f82b8d6bf6652ff7d71a1e27fd2e4efde587fd368662814d6ec1d9ce0" [[package]] name = "event-listener" -version = "5.3.1" +version = "5.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6032be9bd27023a771701cc49f9f053c751055f71efb2e0ae5c15809093675ba" +checksum = "3492acde4c3fc54c845eaab3eed8bd00c7a7d881f78bfc801e43a93dec1331ae" dependencies = [ "concurrent-queue", "parking", @@ -2247,11 +2283,11 @@ dependencies = [ [[package]] name = "event-listener-strategy" -version = "0.5.2" +version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f214dc438f977e6d4e3500aaa277f5ad94ca83fbbd9b1a15713ce2344ccc5a1" +checksum = "3c3e4e0dd3673c1139bf041f3008816d9cf2946bbfac2945c09e523b8d7b05b2" dependencies = [ - "event-listener 5.3.1", + "event-listener 5.4.0", "pin-project-lite", ] @@ -2269,7 +2305,7 @@ dependencies = [ "evm-runtime", "log", "parity-scale-codec", - "primitive-types", + "primitive-types 0.12.2", "rlp", "scale-info", "serde", @@ -2283,7 +2319,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d1da6cedc5cedb4208e59467106db0d1f50db01b920920589f8e672c02fdc04f" dependencies = [ "parity-scale-codec", - "primitive-types", + "primitive-types 0.12.2", "scale-info", "serde", ] @@ -2297,7 +2333,7 @@ dependencies = [ "environmental", "evm-core", "evm-runtime", - "primitive-types", + "primitive-types 0.12.2", ] [[package]] @@ -2309,7 +2345,7 @@ dependencies = [ "auto_impl", "environmental", "evm-core", - "primitive-types", + "primitive-types 0.12.2", "sha3", ] @@ -2331,10 +2367,10 @@ dependencies = [ "blake2 0.10.6", "file-guard", "fs-err", - "prettyplease 0.2.22", + "prettyplease 0.2.29", "proc-macro2", "quote", - "syn 2.0.90", + "syn 2.0.96", ] [[package]] @@ -2351,9 +2387,9 @@ checksum = "2acce4a10f12dc2fb14a218589d4f1f62ef011b2d0cc4b3cb1bba8e94da14649" [[package]] name = "fastrand" -version = "2.1.1" +version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8c02a5121d4ea3eb16a80748c74f5549a5665e4c21333c6098f283870fbdea6" +checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" [[package]] name = "fc-api" @@ -2380,7 +2416,7 @@ dependencies = [ "sp-block-builder", "sp-consensus", "sp-runtime", - "thiserror", + "thiserror 1.0.69", ] [[package]] @@ -2486,7 +2522,7 @@ dependencies = [ "sp-storage 21.0.0", "sp-timestamp", "substrate-prometheus-endpoint", - "thiserror", + "thiserror 1.0.69", "tokio", ] @@ -2529,7 +2565,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e182f7dbc2ef73d9ef67351c5fbbea084729c48362d3ce9dd44c28e32e277fe5" dependencies = [ "libc", - "thiserror", + "thiserror 1.0.69", ] [[package]] @@ -2642,9 +2678,9 @@ checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" [[package]] name = "foldhash" -version = "0.1.3" +version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f81ec6369c545a7d40e4589b5597581fa1c441fe1cce96dd1de43159910a36a2" +checksum = "a0d2fde1f7b3d48b8395d5f2de76c18a528bd6a9cdde438df747bfcba3e05d6f" [[package]] name = "foreign-types" @@ -2685,7 +2721,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8835f84f38484cc86f110a805655697908257fb9a7af005234060891557198e9" dependencies = [ "nonempty", - "thiserror", + "thiserror 1.0.69", ] [[package]] @@ -2694,7 +2730,7 @@ version = "1.0.0-dev" source = "git+https://github.com/opentensor/frontier?rev=635bdac882#635bdac882333afed827053f31ef56ab739f7a2e" dependencies = [ "hex", - "impl-serde", + "impl-serde 0.4.0", "libsecp256k1", "log", "parity-scale-codec", @@ -2868,7 +2904,7 @@ dependencies = [ "sp-storage 21.0.0", "sp-trie", "sp-wasm-interface 21.0.1", - "thiserror", + "thiserror 1.0.69", "thousands", ] @@ -2975,7 +3011,7 @@ dependencies = [ "proc-macro2", "quote", "sp-crypto-hashing 0.1.0 (git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-stable2409)", - "syn 2.0.90", + "syn 2.0.96", ] [[package]] @@ -2988,7 +3024,7 @@ dependencies = [ "proc-macro-crate 3.2.0", "proc-macro2", "quote", - "syn 2.0.90", + "syn 2.0.96", ] [[package]] @@ -3000,7 +3036,7 @@ dependencies = [ "proc-macro-crate 3.2.0", "proc-macro2", "quote", - "syn 2.0.90", + "syn 2.0.96", ] [[package]] @@ -3011,7 +3047,7 @@ checksum = "68672b9ec6fe72d259d3879dc212c5e42e977588cdac830c76f54d9f492aeb58" dependencies = [ "proc-macro2", "quote", - "syn 2.0.90", + "syn 2.0.96", ] [[package]] @@ -3021,7 +3057,7 @@ source = "git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-stable dependencies = [ "proc-macro2", "quote", - "syn 2.0.90", + "syn 2.0.96", ] [[package]] @@ -3106,9 +3142,9 @@ checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c" [[package]] name = "futures" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "645c6916888f6cb6350d2550b80fb63e734897a8498abe35cfb732b6487804b0" +checksum = "65bc07b1a8bc7c85c5f2e110c476c7389b4554ba72af57d8445ea63a576b0876" dependencies = [ "futures-channel", "futures-core", @@ -3147,9 +3183,9 @@ checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e" [[package]] name = "futures-executor" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a576fc72ae164fca6b9db127eaa9a9dda0d61316034f33a0a0d4eda41f02b01d" +checksum = "1e28d1d997f585e54aebc3f97d39e72338912123a67330d723fdbb564d646c9f" dependencies = [ "futures-core", "futures-task", @@ -3176,9 +3212,9 @@ checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6" [[package]] name = "futures-lite" -version = "2.3.0" +version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52527eb5074e35e9339c6b4e8d12600c7128b68fb25dcb9fa9dec18f7c25f3a5" +checksum = "f5edaec856126859abb19ed65f39e90fea3a9574b9707f13539acf4abf7eb532" dependencies = [ "futures-core", "pin-project-lite", @@ -3192,7 +3228,7 @@ checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" dependencies = [ "proc-macro2", "quote", - "syn 2.0.90", + "syn 2.0.96", ] [[package]] @@ -3288,7 +3324,19 @@ checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" dependencies = [ "cfg-if", "libc", - "wasi", + "wasi 0.11.0+wasi-snapshot-preview1", +] + +[[package]] +name = "getrandom" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43a49c392881ce6d5c3b8cb70f98717b7c07aabbdff06687b9030dbfbe2725f8" +dependencies = [ + "cfg-if", + "libc", + "wasi 0.13.3+wasi-0.2.2", + "windows-targets 0.52.6", ] [[package]] @@ -3334,15 +3382,15 @@ dependencies = [ [[package]] name = "gimli" -version = "0.31.0" +version = "0.31.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32085ea23f3234fc7846555e85283ba4de91e21016dc0455a16286d87a292d64" +checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f" [[package]] name = "glob" -version = "0.3.1" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" +checksum = "a8d1add55171497b4705a648c6b583acafb01d58050a51727785f0b2c8e0a2b2" [[package]] name = "governor" @@ -3387,7 +3435,7 @@ dependencies = [ "futures-sink", "futures-util", "http 0.2.12", - "indexmap 2.6.0", + "indexmap 2.7.1", "slab", "tokio", "tokio-util", @@ -3396,17 +3444,17 @@ dependencies = [ [[package]] name = "h2" -version = "0.4.6" +version = "0.4.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "524e8ac6999421f49a846c2d4411f337e53497d8ec55d67753beffa43c5d9205" +checksum = "ccae279728d634d083c00f6099cb58f01cc99c145b84b8be2f6c74618d79922e" dependencies = [ "atomic-waker", "bytes", "fnv", "futures-core", "futures-sink", - "http 1.1.0", - "indexmap 2.6.0", + "http 1.2.0", + "indexmap 2.7.1", "slab", "tokio", "tokio-util", @@ -3430,7 +3478,7 @@ dependencies = [ "pest_derive", "serde", "serde_json", - "thiserror", + "thiserror 1.0.69", ] [[package]] @@ -3478,9 +3526,9 @@ dependencies = [ [[package]] name = "hashbrown" -version = "0.15.0" +version = "0.15.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e087f84d4f86bf4b218b927129862374b72199ae7d8657835f1e89000eea4fb" +checksum = "bf151400ff0baff5465007dd2f3e717f3fe502074ca563069ce3a6629d07b289" dependencies = [ "allocator-api2", "equivalent", @@ -3498,11 +3546,11 @@ dependencies = [ [[package]] name = "hashlink" -version = "0.9.1" +version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ba4ff7128dee98c7dc9794b6a411377e1404dba1c97deb8d1a55297bd25d8af" +checksum = "7382cf6263419f2d8df38c55d7da83da5c18aef87fc7a7fc1fb1e344edfe14c1" dependencies = [ - "hashbrown 0.14.5", + "hashbrown 0.15.2", ] [[package]] @@ -3591,11 +3639,11 @@ dependencies = [ [[package]] name = "home" -version = "0.5.9" +version = "0.5.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3d1354bf6b7235cb4a0576c2619fd4ed18183f689b12b006a0ee7329eeff9a5" +checksum = "589533453244b0995c858700322199b2becb13b627df2851f64a2775d024abcf" dependencies = [ - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] @@ -3622,9 +3670,9 @@ dependencies = [ [[package]] name = "http" -version = "1.1.0" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21b9ddb458710bc376481b842f5da65cdf31522de232c1ca8146abce2a358258" +checksum = "f16ca2af56261c99fba8bac40a10251ce8188205a4c448fbb745a2e4daa76fea" dependencies = [ "bytes", "fnv", @@ -3649,7 +3697,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184" dependencies = [ "bytes", - "http 1.1.0", + "http 1.2.0", ] [[package]] @@ -3660,16 +3708,16 @@ checksum = "793429d76616a256bcb62c2a2ec2bed781c8307e797e2598c50010f2bee2544f" dependencies = [ "bytes", "futures-util", - "http 1.1.0", + "http 1.2.0", "http-body 1.0.1", "pin-project-lite", ] [[package]] name = "httparse" -version = "1.9.5" +version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d71d3574edd2771538b901e6549113b4006ece66150fb69c0fb6d9a2adae946" +checksum = "f2d708df4e7140240a16cd6ab0ab65c972d7433ab77819ea693fde9c43811e2a" [[package]] name = "httpdate" @@ -3685,9 +3733,9 @@ checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" [[package]] name = "hyper" -version = "0.14.30" +version = "0.14.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a152ddd61dfaec7273fe8419ab357f33aee0d914c5f4efbf0d96fa749eea5ec9" +checksum = "41dfc780fdec9373c01bae43289ea34c972e40ee3c9f6b3c8801a35f35586ce7" dependencies = [ "bytes", "futures-channel", @@ -3700,7 +3748,7 @@ dependencies = [ "httpdate", "itoa", "pin-project-lite", - "socket2 0.5.7", + "socket2 0.5.8", "tokio", "tower-service", "tracing", @@ -3709,15 +3757,15 @@ dependencies = [ [[package]] name = "hyper" -version = "1.5.0" +version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbbff0a806a4728c99295b254c8838933b5b082d75e3cb70c8dab21fdfbcfa9a" +checksum = "cc2b571658e38e0c01b1fdca3bbbe93c00d3d71693ff2770043f8c29bc7d6f80" dependencies = [ "bytes", "futures-channel", "futures-util", - "h2 0.4.6", - "http 1.1.0", + "h2 0.4.7", + "http 1.2.0", "http-body 1.0.1", "httparse", "httpdate", @@ -3735,7 +3783,7 @@ checksum = "ec3efd23720e2049821a693cbc7e65ea87c72f1c58ff2f9522ff332b1491e590" dependencies = [ "futures-util", "http 0.2.12", - "hyper 0.14.30", + "hyper 0.14.32", "log", "rustls 0.21.12", "rustls-native-certs", @@ -3751,9 +3799,9 @@ checksum = "df2dcfbe0677734ab2f3ffa7fa7bfd4706bfdc1ef393f2ee30184aed67e631b4" dependencies = [ "bytes", "futures-util", - "http 1.1.0", + "http 1.2.0", "http-body 1.0.1", - "hyper 1.5.0", + "hyper 1.6.0", "pin-project-lite", "tokio", "tower-service", @@ -3897,7 +3945,7 @@ checksum = "1ec89e9337638ecdc08744df490b221a7399bf8d164eb52a665454e60e075ad6" dependencies = [ "proc-macro2", "quote", - "syn 2.0.90", + "syn 2.0.96", ] [[package]] @@ -3960,9 +4008,9 @@ dependencies = [ [[package]] name = "if-watch" -version = "3.2.0" +version = "3.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d6b0422c86d7ce0e97169cc42e04ae643caf278874a7a3c87b8150a220dc7e1e" +checksum = "cdf9d64cfcf380606e64f9a0bcf493616b65331199f984151a6fa11a7b3cde38" dependencies = [ "async-io", "core-foundation", @@ -3971,6 +4019,10 @@ dependencies = [ "if-addrs", "ipnet", "log", + "netlink-packet-core", + "netlink-packet-route", + "netlink-proto", + "netlink-sys", "rtnetlink", "system-configuration", "tokio", @@ -3988,7 +4040,7 @@ dependencies = [ "bytes", "futures", "http 0.2.12", - "hyper 0.14.30", + "hyper 0.14.32", "log", "rand", "tokio", @@ -4005,6 +4057,26 @@ dependencies = [ "parity-scale-codec", ] +[[package]] +name = "impl-codec" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b67aa010c1e3da95bf151bd8b4c059b2ed7e75387cdb969b4f8f2723a43f9941" +dependencies = [ + "parity-scale-codec", +] + +[[package]] +name = "impl-num-traits" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "803d15461ab0dcc56706adf266158acbc44ccf719bf7d0af30705f58b90a4b8c" +dependencies = [ + "integer-sqrt", + "num-traits", + "uint 0.10.0", +] + [[package]] name = "impl-rlp" version = "0.3.0" @@ -4023,15 +4095,24 @@ dependencies = [ "serde", ] +[[package]] +name = "impl-serde" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a143eada6a1ec4aefa5049037a26a6d597bfd64f8c026d07b77133e02b7dd0b" +dependencies = [ + "serde", +] + [[package]] name = "impl-trait-for-tuples" -version = "0.2.2" +version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "11d7a9f6330b71fea57921c9b61c47ee6e84f72d394754eff6163ae67e7395eb" +checksum = "a0eb5a3343abf848c0984fe4604b2b105da9539376e24fc0a3b0007411ae4fd9" dependencies = [ "proc-macro2", "quote", - "syn 1.0.109", + "syn 2.0.96", ] [[package]] @@ -4066,12 +4147,12 @@ dependencies = [ [[package]] name = "indexmap" -version = "2.6.0" +version = "2.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "707907fe3c25f5424cce2cb7e1cbcafee6bdbe735ca90ef77c29e84591e5b9da" +checksum = "8c9c992b02b5b4c94ea26e32fe5bccb7aa7d9f390ab5c1221ff895bc7ea8b652" dependencies = [ "equivalent", - "hashbrown 0.15.0", + "hashbrown 0.15.2", ] [[package]] @@ -4124,7 +4205,7 @@ version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b58db92f96b720de98181bbbe63c831e87005ab460c1bf306eb2622b4707997f" dependencies = [ - "socket2 0.5.7", + "socket2 0.5.8", "widestring", "windows-sys 0.48.0", "winreg", @@ -4132,19 +4213,19 @@ dependencies = [ [[package]] name = "ipnet" -version = "2.10.0" +version = "2.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "187674a687eed5fe42285b40c6291f9a01517d415fad1c3cbc6a9f778af7fcd4" +checksum = "469fb0b9cefa57e3ef31275ee7cacb78f2fdca44e4765491884a2b119d4eb130" [[package]] name = "is-terminal" -version = "0.4.13" +version = "0.4.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "261f68e344040fbd0edea105bef17c66edf46f984ddb1115b775ce31be948f4b" +checksum = "e19b23d53f35ce9f56aebc7d1bb4e6ac1e9c0db7ac85c8d1760c04379edced37" dependencies = [ "hermit-abi 0.4.0", "libc", - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] @@ -4171,11 +4252,20 @@ dependencies = [ "either", ] +[[package]] +name = "itertools" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba291022dbbd398a455acf126c1e341954079855bc60dfdda641363bd6922569" +dependencies = [ + "either", +] + [[package]] name = "itoa" -version = "1.0.11" +version = "1.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" +checksum = "d75a2a4b1b190afb6f5425f10f6a8f959d2ea0b9c2b1d79553551850539e4674" [[package]] name = "jobserver" @@ -4188,18 +4278,19 @@ dependencies = [ [[package]] name = "js-sys" -version = "0.3.70" +version = "0.3.77" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1868808506b929d7b0cfa8f75951347aa71bb21144b7791bae35d9bccfcfe37a" +checksum = "1cfaf33c695fc6e08064efbc1f72ec937429614f25eef83af942d0e227c3a28f" dependencies = [ + "once_cell", "wasm-bindgen", ] [[package]] name = "jsonrpsee" -version = "0.24.7" +version = "0.24.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c5c71d8c1a731cc4227c2f698d377e7848ca12c8a48866fc5e6951c43a4db843" +checksum = "834af00800e962dee8f7bfc0f60601de215e73e78e5497d733a2919da837d3c8" dependencies = [ "jsonrpsee-core", "jsonrpsee-proc-macros", @@ -4211,51 +4302,51 @@ dependencies = [ [[package]] name = "jsonrpsee-core" -version = "0.24.7" +version = "0.24.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2882f6f8acb9fdaec7cefc4fd607119a9bd709831df7d7672a1d3b644628280" +checksum = "76637f6294b04e747d68e69336ef839a3493ca62b35bf488ead525f7da75c5bb" dependencies = [ "async-trait", "bytes", "futures-util", - "http 1.1.0", + "http 1.2.0", "http-body 1.0.1", "http-body-util", "jsonrpsee-types", "parking_lot 0.12.3", "rand", - "rustc-hash 2.0.0", + "rustc-hash 2.1.0", "serde", "serde_json", - "thiserror", + "thiserror 1.0.69", "tokio", "tracing", ] [[package]] name = "jsonrpsee-proc-macros" -version = "0.24.7" +version = "0.24.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c06c01ae0007548e73412c08e2285ffe5d723195bf268bce67b1b77c3bb2a14d" +checksum = "6fcae0c6c159e11541080f1f829873d8f374f81eda0abc67695a13fc8dc1a580" dependencies = [ "heck 0.5.0", "proc-macro-crate 3.2.0", "proc-macro2", "quote", - "syn 2.0.90", + "syn 2.0.96", ] [[package]] name = "jsonrpsee-server" -version = "0.24.7" +version = "0.24.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "82ad8ddc14be1d4290cd68046e7d1d37acd408efed6d3ca08aefcc3ad6da069c" +checksum = "66b7a3df90a1a60c3ed68e7ca63916b53e9afa928e33531e87f61a9c8e9ae87b" dependencies = [ "futures-util", - "http 1.1.0", + "http 1.2.0", "http-body 1.0.1", "http-body-util", - "hyper 1.5.0", + "hyper 1.6.0", "hyper-util", "jsonrpsee-core", "jsonrpsee-types", @@ -4264,7 +4355,7 @@ dependencies = [ "serde", "serde_json", "soketto", - "thiserror", + "thiserror 1.0.69", "tokio", "tokio-stream", "tokio-util", @@ -4274,14 +4365,14 @@ dependencies = [ [[package]] name = "jsonrpsee-types" -version = "0.24.7" +version = "0.24.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a178c60086f24cc35bb82f57c651d0d25d99c4742b4d335de04e97fa1f08a8a1" +checksum = "ddb81adb1a5ae9182df379e374a79e24e992334e7346af4d065ae5b2acb8d4c6" dependencies = [ - "http 1.1.0", + "http 1.2.0", "serde", "serde_json", - "thiserror", + "thiserror 1.0.69", ] [[package]] @@ -4360,15 +4451,15 @@ checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" [[package]] name = "libc" -version = "0.2.159" +version = "0.2.169" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "561d97a539a36e26a9a5fad1ea11a3039a67714694aaa379433e580854bc3dc5" +checksum = "b5aba8db14291edd000dfcc4d620c7ebfb122c613afb886ca8803fa4e128a20a" [[package]] name = "libloading" -version = "0.8.5" +version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4979f22fdb869068da03c9f7528f8297c6fd2606bc3a4affe42e6a823fdb8da4" +checksum = "fc2f4eb4bc735547cfed7c0a4922cbd04a4655978c09b54f1f7b228750664c34" dependencies = [ "cfg-if", "windows-targets 0.52.6", @@ -4376,9 +4467,9 @@ dependencies = [ [[package]] name = "libm" -version = "0.2.8" +version = "0.2.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ec2a862134d2a7d32d7983ddcdd1c4923530833c9f2ea1a44fc5fa473989058" +checksum = "8355be11b20d696c8f18f6cc018c4e372165b1fa8126cef092399c9951984ffa" [[package]] name = "libp2p" @@ -4390,7 +4481,7 @@ dependencies = [ "either", "futures", "futures-timer", - "getrandom", + "getrandom 0.2.15", "instant", "libp2p-allow-block-list", "libp2p-connection-limits", @@ -4414,7 +4505,7 @@ dependencies = [ "multiaddr 0.18.2", "pin-project", "rw-stream-sink", - "thiserror", + "thiserror 1.0.69", ] [[package]] @@ -4455,7 +4546,7 @@ dependencies = [ "libp2p-identity", "log", "multiaddr 0.18.2", - "multihash 0.19.2", + "multihash 0.19.3", "multistream-select", "once_cell", "parking_lot 0.12.3", @@ -4464,7 +4555,7 @@ dependencies = [ "rand", "rw-stream-sink", "smallvec", - "thiserror", + "thiserror 1.0.69", "unsigned-varint 0.7.2", "void", ] @@ -4504,24 +4595,24 @@ dependencies = [ "quick-protobuf", "quick-protobuf-codec", "smallvec", - "thiserror", + "thiserror 1.0.69", "void", ] [[package]] name = "libp2p-identity" -version = "0.2.9" +version = "0.2.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "55cca1eb2bc1fd29f099f3daaab7effd01e1a54b7c577d0ed082521034d912e8" +checksum = "257b5621d159b32282eac446bed6670c39c7dc68a200a992d8f056afa0066f6d" dependencies = [ "bs58 0.5.1", "ed25519-dalek", "hkdf", - "multihash 0.19.2", + "multihash 0.19.3", "quick-protobuf", "rand", "sha2 0.10.8", - "thiserror", + "thiserror 1.0.69", "tracing", "zeroize", ] @@ -4549,8 +4640,8 @@ dependencies = [ "rand", "sha2 0.10.8", "smallvec", - "thiserror", - "uint", + "thiserror 1.0.69", + "uint 0.9.5", "unsigned-varint 0.7.2", "void", ] @@ -4570,7 +4661,7 @@ dependencies = [ "log", "rand", "smallvec", - "socket2 0.5.7", + "socket2 0.5.8", "tokio", "trust-dns-proto 0.22.0", "void", @@ -4606,14 +4697,14 @@ dependencies = [ "libp2p-identity", "log", "multiaddr 0.18.2", - "multihash 0.19.2", + "multihash 0.19.3", "once_cell", "quick-protobuf", "rand", "sha2 0.10.8", "snow", "static_assertions", - "thiserror", + "thiserror 1.0.69", "x25519-dalek", "zeroize", ] @@ -4655,8 +4746,8 @@ dependencies = [ "rand", "ring 0.16.20", "rustls 0.21.12", - "socket2 0.5.7", - "thiserror", + "socket2 0.5.8", + "thiserror 1.0.69", "tokio", ] @@ -4711,7 +4802,7 @@ dependencies = [ "proc-macro-warning 0.4.2", "proc-macro2", "quote", - "syn 2.0.90", + "syn 2.0.96", ] [[package]] @@ -4727,7 +4818,7 @@ dependencies = [ "libp2p-core", "libp2p-identity", "log", - "socket2 0.5.7", + "socket2 0.5.8", "tokio", ] @@ -4745,7 +4836,7 @@ dependencies = [ "ring 0.16.20", "rustls 0.21.12", "rustls-webpki", - "thiserror", + "thiserror 1.0.69", "x509-parser 0.15.1", "yasna", ] @@ -4796,7 +4887,7 @@ dependencies = [ "pin-project-lite", "rw-stream-sink", "soketto", - "thiserror", + "thiserror 1.0.69", "url", "webpki-roots", ] @@ -4810,7 +4901,7 @@ dependencies = [ "futures", "libp2p-core", "log", - "thiserror", + "thiserror 1.0.69", "yamux", ] @@ -4820,9 +4911,9 @@ version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.8.0", "libc", - "redox_syscall 0.5.7", + "redox_syscall 0.5.8", ] [[package]] @@ -4901,9 +4992,9 @@ dependencies = [ [[package]] name = "libz-sys" -version = "1.1.20" +version = "1.1.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2d16453e800a8cf6dd2fc3eb4bc99b786a9b90c663b8559a5b1a041bf89e472" +checksum = "df9b68e50e6e0b26f672573834882eb57759f6db9b3be2ea3c35c91188bb4eaa" dependencies = [ "cc", "pkg-config", @@ -4927,18 +5018,18 @@ checksum = "0717cef1bc8b636c6e1c1bbdefc09e6322da8a9321966e8928ef80d20f7f770f" [[package]] name = "linked_hash_set" -version = "0.1.4" +version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "47186c6da4d81ca383c7c47c1bfc80f4b95f4720514d860a5407aaf4233f9588" +checksum = "bae85b5be22d9843c80e5fc80e9b64c8a3b1f98f867c709956eca3efff4e92e2" dependencies = [ "linked-hash-map", ] [[package]] name = "linregress" -version = "0.5.3" +version = "0.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4de04dcecc58d366391f9920245b85ffa684558a5ef6e7736e754347c3aea9c2" +checksum = "a9eda9dcf4f2a99787827661f312ac3219292549c2ee992bf9a6248ffb066bf7" dependencies = [ "nalgebra", ] @@ -4951,9 +5042,9 @@ checksum = "f051f77a7c8e6957c0696eac88f26b0117e54f52d3fc682ab19397a8812846a4" [[package]] name = "linux-raw-sys" -version = "0.4.14" +version = "0.4.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89" +checksum = "d26c52dbd32dccf2d10cac7725f8eae5296885fb5703b261f7d0a0739ec807ab" [[package]] name = "lioness" @@ -4987,7 +5078,7 @@ dependencies = [ "futures", "futures-timer", "hex-literal", - "indexmap 2.6.0", + "indexmap 2.7.1", "libc", "mockall 0.12.1", "multiaddr 0.17.1", @@ -5008,17 +5099,17 @@ dependencies = [ "simple-dns", "smallvec", "snow", - "socket2 0.5.7", + "socket2 0.5.8", "static_assertions", "str0m", - "thiserror", + "thiserror 1.0.69", "tokio", "tokio-stream", "tokio-tungstenite", "tokio-util", "tracing", "trust-dns-resolver", - "uint", + "uint 0.9.5", "unsigned-varint 0.8.0", "url", "webpki", @@ -5040,9 +5131,9 @@ dependencies = [ [[package]] name = "log" -version = "0.4.22" +version = "0.4.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" +checksum = "04cbf5b083de1c7e0222a7a51dbfdba1cbe1c6ab0b15e29fff3f6c077fd9cd9f" [[package]] name = "lru" @@ -5059,7 +5150,7 @@ version = "0.12.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "234cf4f4a04dc1f57e24b96cc0cd600cf2af460d4161ac5ecdd0af8e1f3b2a38" dependencies = [ - "hashbrown 0.15.0", + "hashbrown 0.15.2", ] [[package]] @@ -5073,9 +5164,9 @@ dependencies = [ [[package]] name = "lz4" -version = "1.28.0" +version = "1.28.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4d1febb2b4a79ddd1980eede06a8f7902197960aa0383ffcfdd62fe723036725" +checksum = "a20b523e860d03443e98350ceaac5e71c6ba89aea7d960769ec3ce37f4de5af4" dependencies = [ "lz4-sys", ] @@ -5108,7 +5199,7 @@ dependencies = [ "macro_magic_core", "macro_magic_macros", "quote", - "syn 2.0.90", + "syn 2.0.96", ] [[package]] @@ -5122,7 +5213,7 @@ dependencies = [ "macro_magic_core_macros", "proc-macro2", "quote", - "syn 2.0.90", + "syn 2.0.96", ] [[package]] @@ -5133,7 +5224,7 @@ checksum = "b02abfe41815b5bd98dbd4260173db2c116dda171dc0fe7838cb206333b83308" dependencies = [ "proc-macro2", "quote", - "syn 2.0.90", + "syn 2.0.96", ] [[package]] @@ -5144,7 +5235,7 @@ checksum = "73ea28ee64b88876bf45277ed9a5817c1817df061a74f2b988971a12570e5869" dependencies = [ "macro_magic_core", "quote", - "syn 2.0.90", + "syn 2.0.96", ] [[package]] @@ -5190,7 +5281,7 @@ version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b2cffa4ad52c6f791f4f8b15f0c05f9824b2ced1160e88cc393d64fff9a8ac64" dependencies = [ - "rustix 0.38.37", + "rustix 0.38.44", ] [[package]] @@ -5263,22 +5354,21 @@ checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" [[package]] name = "miniz_oxide" -version = "0.8.0" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2d80299ef12ff69b16a84bb182e3b9df68b5a91574d3d4fa6e41b65deec4df1" +checksum = "b8402cab7aefae129c6977bb0ff1b8fd9a04eb5b51efc50a70bea51cda0c7924" dependencies = [ "adler2", ] [[package]] name = "mio" -version = "1.0.2" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "80e04d1dcff3aae0704555fe5fee3bcfaf3d1fdf8a7e521d5b9d2b42acb52cec" +checksum = "2886843bf800fba2e3377cff24abf6379b4c4d5c6681eaf9ea5b0d15090450bd" dependencies = [ - "hermit-abi 0.3.9", "libc", - "wasi", + "wasi 0.11.0+wasi-snapshot-preview1", "windows-sys 0.52.0", ] @@ -5303,7 +5393,7 @@ dependencies = [ "rand_chacha", "rand_distr", "subtle 2.6.1", - "thiserror", + "thiserror 1.0.69", "zeroize", ] @@ -5333,7 +5423,7 @@ dependencies = [ "fragile", "lazy_static", "mockall_derive 0.12.1", - "predicates 3.1.2", + "predicates 3.1.3", "predicates-tree", ] @@ -5358,7 +5448,7 @@ dependencies = [ "cfg-if", "proc-macro2", "quote", - "syn 2.0.90", + "syn 2.0.96", ] [[package]] @@ -5391,7 +5481,7 @@ dependencies = [ "data-encoding", "libp2p-identity", "multibase", - "multihash 0.19.2", + "multihash 0.19.3", "percent-encoding", "serde", "static_assertions", @@ -5446,9 +5536,9 @@ dependencies = [ [[package]] name = "multihash" -version = "0.19.2" +version = "0.19.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc41f430805af9d1cf4adae4ed2149c759b877b01d909a1f40256188d09345d2" +checksum = "6b430e7953c29dd6a09afc29ff0bb69c6e306329ee6794700aee27b76a1aea8d" dependencies = [ "core2", "unsigned-varint 0.8.0", @@ -5474,6 +5564,12 @@ version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e5ce46fe64a9d73be07dcbe690a38ce1b293be448fd8ce1e6c1b8062c9f72c6a" +[[package]] +name = "multimap" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "defc4c55412d89136f966bbb339008b474350e5e6e78d2714439c386b3137a03" + [[package]] name = "multistream-select" version = "0.13.0" @@ -5490,13 +5586,12 @@ dependencies = [ [[package]] name = "nalgebra" -version = "0.32.6" +version = "0.33.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b5c17de023a86f59ed79891b2e5d5a94c705dbe904a5b5c9c952ea6221b03e4" +checksum = "26aecdf64b707efd1310e3544d709c5c0ac61c13756046aaaba41be5c4f66a3b" dependencies = [ "approx", "matrixmultiply", - "nalgebra-macros", "num-complex", "num-rational", "num-traits", @@ -5504,17 +5599,6 @@ dependencies = [ "typenum 1.17.0", ] -[[package]] -name = "nalgebra-macros" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "254a5372af8fc138e36684761d3c0cdb758a4410e938babcff1c860ce14ddbfc" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.90", -] - [[package]] name = "names" version = "0.14.0" @@ -5526,9 +5610,9 @@ dependencies = [ [[package]] name = "native-tls" -version = "0.2.12" +version = "0.2.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8614eb2c83d59d1c8cc974dd3f920198647674a0a035e1af1fa58707e317466" +checksum = "0dab59f8e050d5df8e4dd87d9206fb6f65a483e20ac9fda365ade4fab353196c" dependencies = [ "libc", "log", @@ -5556,21 +5640,20 @@ dependencies = [ [[package]] name = "netlink-packet-core" -version = "0.4.2" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "345b8ab5bd4e71a2986663e88c56856699d060e78e152e6e9d7966fcd5491297" +checksum = "72724faf704479d67b388da142b186f916188505e7e0b26719019c525882eda4" dependencies = [ "anyhow", "byteorder", - "libc", "netlink-packet-utils", ] [[package]] name = "netlink-packet-route" -version = "0.12.0" +version = "0.17.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9ea4302b9759a7a88242299225ea3688e63c85ea136371bb6cf94fd674efaab" +checksum = "053998cea5a306971f88580d0829e90f270f940befd7cf928da179d4187a5a66" dependencies = [ "anyhow", "bitflags 1.3.2", @@ -5589,29 +5672,28 @@ dependencies = [ "anyhow", "byteorder", "paste", - "thiserror", + "thiserror 1.0.69", ] [[package]] name = "netlink-proto" -version = "0.10.0" +version = "0.11.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "65b4b14489ab424703c092062176d52ba55485a89c076b4f9db05092b7223aa6" +checksum = "72452e012c2f8d612410d89eea01e2d9b56205274abb35d53f60200b2ec41d60" dependencies = [ "bytes", "futures", "log", "netlink-packet-core", "netlink-sys", - "thiserror", - "tokio", + "thiserror 2.0.11", ] [[package]] name = "netlink-sys" -version = "0.8.6" +version = "0.8.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "416060d346fbaf1f23f9512963e3e878f1a78e707cb699ba9215761754244307" +checksum = "16c903aa70590cb93691bf97a767c8d1d6122d2cc9070433deb3bbf36ce8bd23" dependencies = [ "bytes", "futures", @@ -5628,15 +5710,15 @@ checksum = "a4a43439bf756eed340bdf8feba761e2d50c7d47175d87545cd5cbe4a137c4d1" dependencies = [ "cc", "libc", - "thiserror", + "thiserror 1.0.69", "winapi", ] [[package]] name = "nix" -version = "0.24.3" +version = "0.26.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa52e972a9a719cecb6864fb88568781eb706bac2cd1d4f04a648542dbf78069" +checksum = "598beaf3cc6fdd9a5dfb1630c2800c7acd31df7aaf0f565796fba2b53ca1af1b" dependencies = [ "bitflags 1.3.2", "cfg-if", @@ -5728,7 +5810,7 @@ dependencies = [ "substrate-prometheus-endpoint", "subtensor-custom-rpc", "subtensor-custom-rpc-runtime-api", - "thiserror", + "thiserror 1.0.69", ] [[package]] @@ -5750,7 +5832,7 @@ dependencies = [ "frame-system-benchmarking", "frame-system-rpc-runtime-api", "frame-try-runtime", - "getrandom", + "getrandom 0.2.15", "hex", "log", "pallet-admin-utils", @@ -5973,7 +6055,7 @@ dependencies = [ "proc-macro-crate 3.2.0", "proc-macro2", "quote", - "syn 2.0.90", + "syn 2.0.96", ] [[package]] @@ -5999,9 +6081,9 @@ dependencies = [ [[package]] name = "object" -version = "0.36.4" +version = "0.36.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "084f1a5821ac4c651660a94a7153d27ac9d8a53736203f58b31945ded098070a" +checksum = "62948e14d923ea95ea2c7c86c71013138b66525b86bdc08d2dcc262bdb497b87" dependencies = [ "memchr", ] @@ -6026,12 +6108,9 @@ dependencies = [ [[package]] name = "once_cell" -version = "1.20.1" +version = "1.20.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "82881c4be219ab5faaf2ad5e5e5ecdff8c66bd7402ca3160975c93b24961afd1" -dependencies = [ - "portable-atomic", -] +checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775" [[package]] name = "opaque-debug" @@ -6047,11 +6126,11 @@ checksum = "c08d65885ee38876c4f86fa503fb49d7b507c2b62552df7c70b2fce627e06381" [[package]] name = "openssl" -version = "0.10.68" +version = "0.10.69" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6174bc48f102d208783c2c84bf931bb75927a617866870de8a4ea85597f871f5" +checksum = "f5e534d133a060a3c19daec1eb3e98ec6f4685978834f2dbadfe2ec215bab64e" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.8.0", "cfg-if", "foreign-types", "libc", @@ -6068,20 +6147,20 @@ checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.90", + "syn 2.0.96", ] [[package]] name = "openssl-probe" -version = "0.1.5" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" +checksum = "d05e27ee213611ffe7d6348b942e8f942b37114c00cc03cec254295a4a17852e" [[package]] name = "openssl-src" -version = "300.4.0+3.4.0" +version = "300.4.1+3.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a709e02f2b4aca747929cca5ed248880847c650233cf8b8cdc48f40aaf4898a6" +checksum = "faa4eac4138c62414b5622d1b31c5c304f34b406b013c079c2bbc652fdd6678c" dependencies = [ "cc", ] @@ -6762,7 +6841,7 @@ dependencies = [ "lru 0.8.1", "parity-util-mem-derive", "parking_lot 0.12.3", - "primitive-types", + "primitive-types 0.12.2", "smallvec", "winapi", ] @@ -6833,7 +6912,7 @@ checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8" dependencies = [ "cfg-if", "libc", - "redox_syscall 0.5.7", + "redox_syscall 0.5.8", "smallvec", "windows-targets 0.52.6", ] @@ -6894,20 +6973,20 @@ checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" [[package]] name = "pest" -version = "2.7.13" +version = "2.7.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fdbef9d1d47087a895abd220ed25eb4ad973a5e26f6a4367b038c25e28dfc2d9" +checksum = "8b7cafe60d6cf8e62e1b9b2ea516a089c008945bb5a275416789e7db0bc199dc" dependencies = [ "memchr", - "thiserror", + "thiserror 2.0.11", "ucd-trie", ] [[package]] name = "pest_derive" -version = "2.7.13" +version = "2.7.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4d3a6e3394ec80feb3b6393c725571754c6188490265c61aaf260810d6b95aa0" +checksum = "816518421cfc6887a0d62bf441b6ffb4536fcc926395a69e1a85852d4363f57e" dependencies = [ "pest", "pest_generator", @@ -6915,22 +6994,22 @@ dependencies = [ [[package]] name = "pest_generator" -version = "2.7.13" +version = "2.7.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94429506bde1ca69d1b5601962c73f4172ab4726571a59ea95931218cb0e930e" +checksum = "7d1396fd3a870fc7838768d171b4616d5c91f6cc25e377b673d714567d99377b" dependencies = [ "pest", "pest_meta", "proc-macro2", "quote", - "syn 2.0.90", + "syn 2.0.96", ] [[package]] name = "pest_meta" -version = "2.7.13" +version = "2.7.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac8a071862e93690b6e34e9a5fb8e33ff3734473ac0245b27232222c4906a33f" +checksum = "e1e58089ea25d717bfd31fb534e4f3afcc2cc569c70de3e239778991ea3b7dea" dependencies = [ "once_cell", "pest", @@ -6944,34 +7023,34 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b4c5cc86750666a3ed20bdaf5ca2a0344f9c67674cae0515bec2da16fbaa47db" dependencies = [ "fixedbitset", - "indexmap 2.6.0", + "indexmap 2.7.1", ] [[package]] name = "pin-project" -version = "1.1.5" +version = "1.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6bf43b791c5b9e34c3d182969b4abb522f9343702850a2e57f460d00d09b4b3" +checksum = "1e2ec53ad785f4d35dac0adea7f7dc6f1bb277ad84a680c7afefeae05d1f5916" dependencies = [ "pin-project-internal", ] [[package]] name = "pin-project-internal" -version = "1.1.5" +version = "1.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f38a4412a78282e09a2cf38d195ea5420d15ba0602cb375210efbc877243965" +checksum = "d56a66c0c55993aa927429d0f8a0abfd74f084e4d9c192cffed01e418d83eefb" dependencies = [ "proc-macro2", "quote", - "syn 2.0.90", + "syn 2.0.96", ] [[package]] name = "pin-project-lite" -version = "0.2.14" +version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bda66fc9667c18cb2758a2ac84d1167245054bcf85d5d1aaa6923f45801bdd02" +checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b" [[package]] name = "pin-utils" @@ -7004,7 +7083,7 @@ dependencies = [ "libc", "log", "polkavm-assembler", - "polkavm-common", + "polkavm-common 0.9.0", "polkavm-linux-raw", ] @@ -7026,13 +7105,28 @@ dependencies = [ "log", ] +[[package]] +name = "polkavm-common" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "31ff33982a807d8567645d4784b9b5d7ab87bcb494f534a57cadd9012688e102" + [[package]] name = "polkavm-derive" version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ae8c4bea6f3e11cd89bb18bcdddac10bd9a24015399bd1c485ad68a985a19606" dependencies = [ - "polkavm-derive-impl-macro", + "polkavm-derive-impl-macro 0.9.0", +] + +[[package]] +name = "polkavm-derive" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2eb703f3b6404c13228402e98a5eae063fd16b8f58afe334073ec105ee4117e" +dependencies = [ + "polkavm-derive-impl-macro 0.18.0", ] [[package]] @@ -7041,10 +7135,22 @@ version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5c4fdfc49717fb9a196e74a5d28e0bc764eb394a2c803eb11133a31ac996c60c" dependencies = [ - "polkavm-common", + "polkavm-common 0.9.0", + "proc-macro2", + "quote", + "syn 2.0.96", +] + +[[package]] +name = "polkavm-derive-impl" +version = "0.18.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2f2116a92e6e96220a398930f4c8a6cda1264206f3e2034fc9982bfd93f261f7" +dependencies = [ + "polkavm-common 0.18.0", "proc-macro2", "quote", - "syn 2.0.90", + "syn 2.0.96", ] [[package]] @@ -7053,8 +7159,18 @@ version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8ba81f7b5faac81e528eb6158a6f3c9e0bb1008e0ffa19653bc8dea925ecb429" dependencies = [ - "polkavm-derive-impl", - "syn 2.0.90", + "polkavm-derive-impl 0.9.0", + "syn 2.0.96", +] + +[[package]] +name = "polkavm-derive-impl-macro" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48c16669ddc7433e34c1007d31080b80901e3e8e523cb9d4b441c3910cf9294b" +dependencies = [ + "polkavm-derive-impl 0.18.1", + "syn 2.0.96", ] [[package]] @@ -7067,7 +7183,7 @@ dependencies = [ "hashbrown 0.14.5", "log", "object 0.32.2", - "polkavm-common", + "polkavm-common 0.9.0", "regalloc2 0.9.3", "rustc-demangle", ] @@ -7080,15 +7196,15 @@ checksum = "26e85d3456948e650dff0cfc85603915847faf893ed1e66b020bb82ef4557120" [[package]] name = "polling" -version = "3.7.3" +version = "3.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc2790cd301dec6cd3b7a025e4815cf825724a51c98dccfe6a3e55f05ffb6511" +checksum = "a604568c3202727d1507653cb121dbd627a58684eb09a820fd746bee38b4442f" dependencies = [ "cfg-if", "concurrent-queue", "hermit-abi 0.4.0", "pin-project-lite", - "rustix 0.38.37", + "rustix 0.38.44", "tracing", "windows-sys 0.59.0", ] @@ -7118,9 +7234,9 @@ dependencies = [ [[package]] name = "portable-atomic" -version = "1.9.0" +version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc9c68a3f6da06753e9335d63e27f6b9754dd1920d941135b7ea8224f141adb2" +checksum = "280dc24453071f1b63954171985a0b0d30058d287960968b9b2aca264c8d4ee6" [[package]] name = "powerfmt" @@ -7153,9 +7269,9 @@ dependencies = [ [[package]] name = "predicates" -version = "3.1.2" +version = "3.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e9086cc7640c29a356d1a29fd134380bee9d8f79a17410aa76e7ad295f42c97" +checksum = "a5d19ee57562043d37e82899fade9a22ebab7be9cef5026b07fda9cdd4293573" dependencies = [ "anstyle", "predicates-core", @@ -7163,15 +7279,15 @@ dependencies = [ [[package]] name = "predicates-core" -version = "1.0.8" +version = "1.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae8177bee8e75d6846599c6b9ff679ed51e882816914eec639944d7c9aa11931" +checksum = "727e462b119fe9c93fd0eb1429a5f7647394014cf3c04ab2c0350eeb09095ffa" [[package]] name = "predicates-tree" -version = "1.0.11" +version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41b740d195ed3166cd147c8047ec98db0e22ec019eb8eeb76d343b795304fb13" +checksum = "72dd2d6d381dfb73a193c7fca536518d7caee39fc8503f74e7dc0be0531b425c" dependencies = [ "predicates-core", "termtree", @@ -7189,12 +7305,12 @@ dependencies = [ [[package]] name = "prettyplease" -version = "0.2.22" +version = "0.2.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "479cf940fbbb3426c32c5d5176f62ad57549a0bb84773423ba8be9d089f5faba" +checksum = "6924ced06e1f7dfe3fa48d57b9f74f55d8915f5036121bef647ef4b204895fac" dependencies = [ "proc-macro2", - "syn 2.0.90", + "syn 2.0.96", ] [[package]] @@ -7204,11 +7320,23 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0b34d9fd68ae0b74a41b21c03c2f62847aa0ffea044eee893b4c140b37e244e2" dependencies = [ "fixed-hash", - "impl-codec", + "impl-codec 0.6.0", "impl-rlp", - "impl-serde", + "impl-serde 0.4.0", "scale-info", - "uint", + "uint 0.9.5", +] + +[[package]] +name = "primitive-types" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d15600a7d856470b7d278b3fe0e311fe28c2526348549f8ef2ff7db3299c87f5" +dependencies = [ + "fixed-hash", + "impl-codec 0.7.0", + "impl-num-traits", + "uint 0.10.0", ] [[package]] @@ -7217,7 +7345,7 @@ version = "1.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e17d47ce914bf4de440332250b0edd23ce48c005f59fab39d3335866b114f11a" dependencies = [ - "thiserror", + "thiserror 1.0.69", "toml 0.5.11", ] @@ -7262,7 +7390,7 @@ checksum = "3d1eaa7fa0aa1929ffdf7eeb6eac234dde6268914a14ad44d23521ab6a9b258e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.90", + "syn 2.0.96", ] [[package]] @@ -7273,14 +7401,14 @@ checksum = "834da187cfe638ae8abb0203f0b33e5ccdb02a28e7199f2f47b3e2754f50edca" dependencies = [ "proc-macro2", "quote", - "syn 2.0.90", + "syn 2.0.96", ] [[package]] name = "proc-macro2" -version = "1.0.92" +version = "1.0.93" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37d3544b3f2748c54e147655edb5025752e2303145b5aefb3c3ea2c78b973bb0" +checksum = "60946a68e5f9d28b0dc1c21bb8a97ee7d018a8b322fa57838ba31cc878e22d99" dependencies = [ "unicode-ident", ] @@ -7301,7 +7429,7 @@ dependencies = [ "quote", "regex", "sp-crypto-hashing 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 2.0.90", + "syn 2.0.96", ] [[package]] @@ -7315,7 +7443,7 @@ dependencies = [ "lazy_static", "memchr", "parking_lot 0.12.3", - "thiserror", + "thiserror 1.0.69", ] [[package]] @@ -7338,7 +7466,7 @@ checksum = "440f724eba9f6996b75d63681b0a92b06947f1457076d503a4d2e2c8f56442b8" dependencies = [ "proc-macro2", "quote", - "syn 2.0.90", + "syn 2.0.96", ] [[package]] @@ -7372,7 +7500,7 @@ dependencies = [ "itertools 0.10.5", "lazy_static", "log", - "multimap", + "multimap 0.8.3", "petgraph", "prettyplease 0.1.25", "prost 0.11.9", @@ -7391,16 +7519,16 @@ checksum = "22505a5c94da8e3b7c2996394d1c933236c4d743e81a410bcca4e6989fc066a4" dependencies = [ "bytes", "heck 0.5.0", - "itertools 0.11.0", + "itertools 0.12.1", "log", - "multimap", + "multimap 0.10.0", "once_cell", "petgraph", - "prettyplease 0.2.22", + "prettyplease 0.2.29", "prost 0.12.6", "prost-types 0.12.6", "regex", - "syn 2.0.90", + "syn 2.0.96", "tempfile", ] @@ -7424,10 +7552,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "81bddcdb20abf9501610992b6759a4c888aef7d1a7247ef75e2404275ac24af1" dependencies = [ "anyhow", - "itertools 0.11.0", + "itertools 0.12.1", "proc-macro2", "quote", - "syn 2.0.90", + "syn 2.0.96", ] [[package]] @@ -7450,24 +7578,24 @@ dependencies = [ [[package]] name = "psm" -version = "0.1.23" +version = "0.1.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa37f80ca58604976033fae9515a8a2989fc13797d953f7c04fb8fa36a11f205" +checksum = "200b9ff220857e53e184257720a14553b2f4aa02577d2ed9842d45d4b9654810" dependencies = [ "cc", ] [[package]] name = "quanta" -version = "0.12.3" +version = "0.12.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e5167a477619228a0b284fac2674e3c388cba90631d7b7de620e6f1fcd08da5" +checksum = "3bd1fe6824cea6538803de3ff1bc0cf3949024db3d43c9643024bfb33a807c0e" dependencies = [ "crossbeam-utils", "libc", "once_cell", "raw-cpuid", - "wasi", + "wasi 0.11.0+wasi-snapshot-preview1", "web-sys", "winapi", ] @@ -7496,7 +7624,7 @@ dependencies = [ "asynchronous-codec", "bytes", "quick-protobuf", - "thiserror", + "thiserror 1.0.69", "unsigned-varint 0.7.2", ] @@ -7512,7 +7640,7 @@ dependencies = [ "quinn-udp 0.3.2", "rustc-hash 1.1.0", "rustls 0.20.9", - "thiserror", + "thiserror 1.0.69", "tokio", "tracing", "webpki", @@ -7531,7 +7659,7 @@ dependencies = [ "quinn-udp 0.4.1", "rustc-hash 1.1.0", "rustls 0.21.12", - "thiserror", + "thiserror 1.0.69", "tokio", "tracing", ] @@ -7548,7 +7676,7 @@ dependencies = [ "rustc-hash 1.1.0", "rustls 0.20.9", "slab", - "thiserror", + "thiserror 1.0.69", "tinyvec", "tracing", "webpki", @@ -7566,7 +7694,7 @@ dependencies = [ "rustc-hash 1.1.0", "rustls 0.21.12", "slab", - "thiserror", + "thiserror 1.0.69", "tinyvec", "tracing", ] @@ -7592,16 +7720,16 @@ checksum = "055b4e778e8feb9f93c4e439f71dc2156ef13360b432b799e179a8c4cdf0b1d7" dependencies = [ "bytes", "libc", - "socket2 0.5.7", + "socket2 0.5.8", "tracing", "windows-sys 0.48.0", ] [[package]] name = "quote" -version = "1.0.37" +version = "1.0.38" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af" +checksum = "0e4dccaaaf89514f546c693ddc140f729f958c247918a13380cccc6078391acc" dependencies = [ "proc-macro2", ] @@ -7639,7 +7767,7 @@ version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" dependencies = [ - "getrandom", + "getrandom 0.2.15", ] [[package]] @@ -7663,11 +7791,11 @@ dependencies = [ [[package]] name = "raw-cpuid" -version = "11.2.0" +version = "11.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ab240315c661615f2ee9f0f2cd32d5a7343a84d5ebcccb99d46e6637565e7b0" +checksum = "c6928fa44c097620b706542d428957635951bade7143269085389d42c8a4927e" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.8.0", ] [[package]] @@ -7719,11 +7847,11 @@ dependencies = [ [[package]] name = "redox_syscall" -version = "0.5.7" +version = "0.5.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b6dfecf2c74bce2466cabf93f6664d6998a69eb21e39f4207930065b27b771f" +checksum = "03a862b389f93e68874fbf580b9de08dd02facb9a788ebadaf4a3fd33cf58834" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.8.0", ] [[package]] @@ -7732,9 +7860,9 @@ version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ba009ff324d1fc1b900bd1fdb31564febe58a8ccc8a6fdbb93b543d33b13ca43" dependencies = [ - "getrandom", + "getrandom 0.2.15", "libredox", - "thiserror", + "thiserror 1.0.69", ] [[package]] @@ -7754,7 +7882,7 @@ checksum = "bcc303e793d3734489387d205e9b186fac9c6cfacedd98cbb2e8a5943595f3e6" dependencies = [ "proc-macro2", "quote", - "syn 2.0.90", + "syn 2.0.96", ] [[package]] @@ -7784,13 +7912,13 @@ dependencies = [ [[package]] name = "regex" -version = "1.11.0" +version = "1.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38200e5ee88914975b69f657f0801b6f6dccafd44fd9326302a4aaeecfacb1d8" +checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191" dependencies = [ "aho-corasick", "memchr", - "regex-automata 0.4.8", + "regex-automata 0.4.9", "regex-syntax 0.8.5", ] @@ -7805,9 +7933,9 @@ dependencies = [ [[package]] name = "regex-automata" -version = "0.4.8" +version = "0.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "368758f23274712b504848e9d5a6f010445cc8b87a7cdb4d7cbee666c1288da3" +checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908" dependencies = [ "aho-corasick", "memchr", @@ -7869,7 +7997,7 @@ checksum = "c17fa4cb658e3583423e915b9f3acc01cceaee1860e33d59ebae66adc3a2dc0d" dependencies = [ "cc", "cfg-if", - "getrandom", + "getrandom 0.2.15", "libc", "spin 0.9.8", "untrusted 0.9.0", @@ -7936,16 +8064,19 @@ dependencies = [ [[package]] name = "rtnetlink" -version = "0.10.1" +version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "322c53fd76a18698f1c27381d58091de3a043d356aa5bd0d510608b565f469a0" +checksum = "7a552eb82d19f38c3beed3f786bd23aa434ceb9ac43ab44419ca6d67a7e186c0" dependencies = [ "futures", "log", + "netlink-packet-core", "netlink-packet-route", + "netlink-packet-utils", "netlink-proto", + "netlink-sys", "nix", - "thiserror", + "thiserror 1.0.69", "tokio", ] @@ -7973,9 +8104,9 @@ checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" [[package]] name = "rustc-hash" -version = "2.0.0" +version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "583034fd73374156e66797ed8e5b0d5690409c9226b22d87cb7f19821c05d152" +checksum = "c7fb8039b3032c191086b10f11f319a6e99e1e82889c5cc6046f515c9db1d497" [[package]] name = "rustc-hex" @@ -7998,7 +8129,7 @@ version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92" dependencies = [ - "semver 1.0.23", + "semver 1.0.25", ] [[package]] @@ -8026,15 +8157,15 @@ dependencies = [ [[package]] name = "rustix" -version = "0.38.37" +version = "0.38.44" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8acb788b847c24f28525660c4d7758620a7210875711f79e7f663cc152726811" +checksum = "fdb5bc1ae2baa591800df16c9ca78619bf65c0488b41b96ccec5d11220d8c154" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.8.0", "errno", "libc", - "linux-raw-sys 0.4.14", - "windows-sys 0.52.0", + "linux-raw-sys 0.4.15", + "windows-sys 0.59.0", ] [[package]] @@ -8093,9 +8224,9 @@ dependencies = [ [[package]] name = "rustversion" -version = "1.0.17" +version = "1.0.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "955d28af4278de8121b7ebeb796b6a45735dc01436d898801014aced2773a3d6" +checksum = "f7c45b9784283f1b2e7fb61b42047c2fd678ef0960d4f6f1eba131594cc369d4" [[package]] name = "rw-stream-sink" @@ -8110,9 +8241,9 @@ dependencies = [ [[package]] name = "ryu" -version = "1.0.18" +version = "1.0.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" +checksum = "6ea1a2d0a644769cc99faa24c3ad26b379b786fe7c36fd3c546254801650e6dd" [[package]] name = "safe-math" @@ -8134,9 +8265,9 @@ dependencies = [ [[package]] name = "safe_arch" -version = "0.7.2" +version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3460605018fdc9612bce72735cba0d27efbcd9904780d44c7e3a9948f96148a" +checksum = "96b02de82ddbe1b636e6170c21be622223aea188ef2e139be0a5b219ec215323" dependencies = [ "bytemuck", ] @@ -8158,7 +8289,7 @@ dependencies = [ "log", "sp-core", "sp-wasm-interface 21.0.1", - "thiserror", + "thiserror 1.0.69", ] [[package]] @@ -8233,7 +8364,7 @@ dependencies = [ "proc-macro-crate 3.2.0", "proc-macro2", "quote", - "syn 2.0.90", + "syn 2.0.96", ] [[package]] @@ -8273,7 +8404,7 @@ dependencies = [ "sp-panic-handler", "sp-runtime", "sp-version", - "thiserror", + "thiserror 1.0.69", "tokio", ] @@ -8351,7 +8482,7 @@ dependencies = [ "sp-runtime", "sp-state-machine", "substrate-prometheus-endpoint", - "thiserror", + "thiserror 1.0.69", ] [[package]] @@ -8380,7 +8511,7 @@ dependencies = [ "sp-keystore", "sp-runtime", "substrate-prometheus-endpoint", - "thiserror", + "thiserror 1.0.69", ] [[package]] @@ -8416,7 +8547,7 @@ dependencies = [ "sp-keystore", "sp-runtime", "substrate-prometheus-endpoint", - "thiserror", + "thiserror 1.0.69", ] [[package]] @@ -8473,7 +8604,7 @@ dependencies = [ "sp-keystore", "sp-runtime", "substrate-prometheus-endpoint", - "thiserror", + "thiserror 1.0.69", ] [[package]] @@ -8493,7 +8624,7 @@ dependencies = [ "sp-blockchain", "sp-core", "sp-runtime", - "thiserror", + "thiserror 1.0.69", ] [[package]] @@ -8528,7 +8659,7 @@ dependencies = [ "sp-runtime", "sp-timestamp", "substrate-prometheus-endpoint", - "thiserror", + "thiserror 1.0.69", ] [[package]] @@ -8586,7 +8717,7 @@ dependencies = [ "sc-allocator", "sp-maybe-compressed-blob", "sp-wasm-interface 21.0.1", - "thiserror", + "thiserror 1.0.69", "wasm-instrument", ] @@ -8647,7 +8778,7 @@ dependencies = [ "sp-application-crypto", "sp-core", "sp-keystore", - "thiserror", + "thiserror 1.0.69", ] [[package]] @@ -8676,7 +8807,7 @@ dependencies = [ "sp-keystore", "sp-mixnet", "sp-runtime", - "thiserror", + "thiserror 1.0.69", ] [[package]] @@ -8721,7 +8852,7 @@ dependencies = [ "sp-core", "sp-runtime", "substrate-prometheus-endpoint", - "thiserror", + "thiserror 1.0.69", "tokio", "tokio-stream", "unsigned-varint 0.7.2", @@ -8785,7 +8916,7 @@ dependencies = [ "sp-blockchain", "sp-core", "sp-runtime", - "thiserror", + "thiserror 1.0.69", ] [[package]] @@ -8820,7 +8951,7 @@ dependencies = [ "sp-core", "sp-runtime", "substrate-prometheus-endpoint", - "thiserror", + "thiserror 1.0.69", "tokio", "tokio-stream", ] @@ -8855,9 +8986,9 @@ dependencies = [ "litep2p", "log", "multiaddr 0.18.2", - "multihash 0.19.2", + "multihash 0.19.3", "rand", - "thiserror", + "thiserror 1.0.69", "zeroize", ] @@ -8871,7 +9002,7 @@ dependencies = [ "fnv", "futures", "futures-timer", - "hyper 0.14.30", + "hyper 0.14.32", "hyper-rustls", "log", "num_cpus", @@ -8953,7 +9084,7 @@ dependencies = [ "sp-rpc", "sp-runtime", "sp-version", - "thiserror", + "thiserror 1.0.69", ] [[package]] @@ -8965,9 +9096,9 @@ dependencies = [ "forwarded-header-value", "futures", "governor", - "http 1.1.0", + "http 1.2.0", "http-body-util", - "hyper 1.5.0", + "hyper 1.6.0", "ip_network", "jsonrpsee", "log", @@ -9007,7 +9138,7 @@ dependencies = [ "sp-rpc", "sp-runtime", "sp-version", - "thiserror", + "thiserror 1.0.69", "tokio", "tokio-stream", ] @@ -9070,7 +9201,7 @@ dependencies = [ "static_init", "substrate-prometheus-endpoint", "tempfile", - "thiserror", + "thiserror 1.0.69", "tokio", "tracing", "tracing-futures", @@ -9092,7 +9223,7 @@ name = "sc-sysinfo" version = "38.0.0" source = "git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-stable2409#87971b3e92721bdf10bf40b410eaae779d494ca0" dependencies = [ - "derive_more", + "derive_more 0.99.18", "futures", "libc", "log", @@ -9124,7 +9255,7 @@ dependencies = [ "sc-utils", "serde", "serde_json", - "thiserror", + "thiserror 1.0.69", "wasm-timer", ] @@ -9151,10 +9282,10 @@ dependencies = [ "sp-rpc", "sp-runtime", "sp-tracing 17.0.1", - "thiserror", + "thiserror 1.0.69", "tracing", "tracing-log", - "tracing-subscriber 0.3.18", + "tracing-subscriber 0.3.19", ] [[package]] @@ -9165,7 +9296,7 @@ dependencies = [ "proc-macro-crate 3.2.0", "proc-macro2", "quote", - "syn 2.0.90", + "syn 2.0.96", ] [[package]] @@ -9192,7 +9323,7 @@ dependencies = [ "sp-tracing 17.0.1", "sp-transaction-pool", "substrate-prometheus-endpoint", - "thiserror", + "thiserror 1.0.69", ] [[package]] @@ -9208,7 +9339,7 @@ dependencies = [ "sp-blockchain", "sp-core", "sp-runtime", - "thiserror", + "thiserror 1.0.69", ] [[package]] @@ -9242,7 +9373,7 @@ version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e98f3262c250d90e700bb802eb704e1f841e03331c2eb815e46516c4edbf5b27" dependencies = [ - "derive_more", + "derive_more 0.99.18", "parity-scale-codec", "scale-bits", "scale-type-resolver", @@ -9251,13 +9382,13 @@ dependencies = [ [[package]] name = "scale-info" -version = "2.11.3" +version = "2.11.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eca070c12893629e2cc820a9761bedf6ce1dcddc9852984d1dc734b8bd9bd024" +checksum = "346a3b32eba2640d17a9cb5927056b08f3de90f65b72fe09402c2ad07d684d0b" dependencies = [ "bitvec", "cfg-if", - "derive_more", + "derive_more 1.0.0", "parity-scale-codec", "scale-info-derive", "serde", @@ -9265,14 +9396,14 @@ dependencies = [ [[package]] name = "scale-info-derive" -version = "2.11.3" +version = "2.11.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d35494501194174bda522a32605929eefc9ecf7e0a326c26db1fdd85881eb62" +checksum = "c6630024bf739e2179b91fb424b28898baf819414262c5d376677dbff1fe7ebf" dependencies = [ "proc-macro-crate 3.2.0", "proc-macro2", "quote", - "syn 1.0.109", + "syn 2.0.96", ] [[package]] @@ -9283,18 +9414,18 @@ checksum = "f0cded6518aa0bd6c1be2b88ac81bf7044992f0f154bfbabd5ad34f43512abcb" [[package]] name = "schannel" -version = "0.1.24" +version = "0.1.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e9aaafd5a2b6e3d657ff009d82fbd630b6bd54dd4eb06f21693925cdf80f9b8b" +checksum = "1f29ebaa345f945cec9fbbc532eb307f0fdad8161f281b6369539c8d84876b3d" dependencies = [ "windows-sys 0.59.0", ] [[package]] name = "schnellru" -version = "0.2.3" +version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c9a8ef13a93c54d20580de1e5c413e624e53121d42fc7e2c11d10ef7f8b02367" +checksum = "356285bbf17bea63d9e52e96bd18f039672ac92b55b8cb997d6162a2a37d1649" dependencies = [ "ahash 0.8.11", "cfg-if", @@ -9354,7 +9485,7 @@ dependencies = [ "log", "rand", "slab", - "thiserror", + "thiserror 1.0.69", ] [[package]] @@ -9405,7 +9536,7 @@ version = "2.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "897b2245f0b511c87893af39b033e5ca9cce68824c4d7e7630b5a1d339658d02" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.8.0", "core-foundation", "core-foundation-sys", "libc", @@ -9414,9 +9545,9 @@ dependencies = [ [[package]] name = "security-framework-sys" -version = "2.12.0" +version = "2.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea4a292869320c0272d7bc55a5a6aafaff59b4f63404a003887b679a2e05b4b6" +checksum = "49db231d56a190491cb4aeda9527f1ad45345af50b0851622a7adb8c03b01c32" dependencies = [ "core-foundation-sys", "libc", @@ -9442,9 +9573,9 @@ dependencies = [ [[package]] name = "semver" -version = "1.0.23" +version = "1.0.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61697e0a1c7e512e84a621326239844a24d8207b4669b41bc18b32ea5cbf988b" +checksum = "f79dfe2d285b0488816f30e700a7438c5a73d816b5b7d3ac72fbc48b0d185e03" dependencies = [ "serde", ] @@ -9463,9 +9594,9 @@ checksum = "cd0b0ec5f1c1ca621c432a25813d8d60c88abe6d3e08a3eb9cf37d97a0fe3d73" [[package]] name = "serde" -version = "1.0.216" +version = "1.0.217" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b9781016e935a97e8beecf0c933758c97a5520d32930e460142b4cd80c6338e" +checksum = "02fc4265df13d6fa1d00ecff087228cc0a2b5f3c0e87e258d8b94a156e984c70" dependencies = [ "serde_derive", ] @@ -9500,20 +9631,20 @@ dependencies = [ [[package]] name = "serde_derive" -version = "1.0.216" +version = "1.0.217" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "46f859dbbf73865c6627ed570e78961cd3ac92407a2d117204c49232485da55e" +checksum = "5a9bf7cf98d04a2b28aead066b7496853d4779c9cc183c440dbac457641e19a0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.90", + "syn 2.0.96", ] [[package]] name = "serde_json" -version = "1.0.128" +version = "1.0.138" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ff5456707a1de34e7e37f2a6fd3d3f808c318259cbd01ab6377795054b483d8" +checksum = "d434192e7da787e94a6ea7e9670b26a036d0ca41e0b7efb2676dd32bae872949" dependencies = [ "itoa", "memchr", @@ -9567,7 +9698,7 @@ dependencies = [ "darling", "proc-macro2", "quote", - "syn 2.0.90", + "syn 2.0.96", ] [[package]] @@ -9691,9 +9822,9 @@ dependencies = [ [[package]] name = "simba" -version = "0.8.1" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "061507c94fc6ab4ba1c9a0305018408e312e17c041eb63bef8aa726fa33aceae" +checksum = "b3a386a501cd104797982c15ae17aafe8b9261315b5d07e3ec803f2ea26be0fa" dependencies = [ "approx", "num-complex", @@ -9708,7 +9839,7 @@ version = "0.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cae9a3fcdadafb6d97f4c0e007e4247b114ee0f119f650c3cbf3a8b3a1479694" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.8.0", ] [[package]] @@ -9779,9 +9910,9 @@ dependencies = [ [[package]] name = "socket2" -version = "0.5.7" +version = "0.5.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce305eb0b4296696835b71df73eb912e0f1ffd2556a501fcede6e0c50349191c" +checksum = "c970269d99b64e60ec3bd6ad27270092a5394c4e309314b18ae3fe575695fbe8" dependencies = [ "libc", "windows-sys 0.52.0", @@ -9789,14 +9920,14 @@ dependencies = [ [[package]] name = "soketto" -version = "0.8.0" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37468c595637c10857701c990f93a40ce0e357cedb0953d1c26c8d8027f9bb53" +checksum = "2e859df029d160cb88608f5d7df7fb4753fd20fdfb4de5644f3d8b8440841721" dependencies = [ "base64 0.22.1", "bytes", "futures", - "http 1.1.0", + "http 1.2.0", "httparse", "log", "rand", @@ -9822,7 +9953,7 @@ dependencies = [ "sp-state-machine", "sp-trie", "sp-version", - "thiserror", + "thiserror 1.0.69", ] [[package]] @@ -9836,7 +9967,7 @@ dependencies = [ "proc-macro-crate 3.2.0", "proc-macro2", "quote", - "syn 2.0.90", + "syn 2.0.96", ] [[package]] @@ -9899,7 +10030,7 @@ dependencies = [ "sp-database", "sp-runtime", "sp-state-machine", - "thiserror", + "thiserror 1.0.69", "tracing", ] @@ -9915,7 +10046,7 @@ dependencies = [ "sp-inherents", "sp-runtime", "sp-state-machine", - "thiserror", + "thiserror 1.0.69", ] [[package]] @@ -9995,7 +10126,7 @@ dependencies = [ "futures", "hash-db", "hash256-std-hasher", - "impl-serde", + "impl-serde 0.4.0", "itertools 0.11.0", "k256", "libsecp256k1", @@ -10005,7 +10136,7 @@ dependencies = [ "parity-scale-codec", "parking_lot 0.12.3", "paste", - "primitive-types", + "primitive-types 0.12.2", "rand", "scale-info", "schnorrkel", @@ -10020,7 +10151,7 @@ dependencies = [ "sp-storage 21.0.0", "ss58-registry", "substrate-bip39", - "thiserror", + "thiserror 1.0.69", "tracing", "w3f-bls", "zeroize", @@ -10029,7 +10160,7 @@ dependencies = [ [[package]] name = "sp-crypto-ec-utils" version = "0.10.0" -source = "git+https://github.com/paritytech/polkadot-sdk#8614dc0e055d06de4a3774ac1da0a422b33f34e2" +source = "git+https://github.com/paritytech/polkadot-sdk#80e30ec3cdccae8e9099bd67840ff8737b043496" dependencies = [ "ark-bls12-377", "ark-bls12-377-ext", @@ -10100,7 +10231,7 @@ source = "git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-stable dependencies = [ "quote", "sp-crypto-hashing 0.1.0 (git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-stable2409)", - "syn 2.0.90", + "syn 2.0.96", ] [[package]] @@ -10119,23 +10250,23 @@ source = "git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-stable dependencies = [ "proc-macro2", "quote", - "syn 2.0.90", + "syn 2.0.96", ] [[package]] name = "sp-debug-derive" version = "14.0.0" -source = "git+https://github.com/paritytech/polkadot-sdk#8614dc0e055d06de4a3774ac1da0a422b33f34e2" +source = "git+https://github.com/paritytech/polkadot-sdk#80e30ec3cdccae8e9099bd67840ff8737b043496" dependencies = [ "proc-macro2", "quote", - "syn 2.0.90", + "syn 2.0.96", ] [[package]] name = "sp-externalities" version = "0.25.0" -source = "git+https://github.com/paritytech/polkadot-sdk#8614dc0e055d06de4a3774ac1da0a422b33f34e2" +source = "git+https://github.com/paritytech/polkadot-sdk#80e30ec3cdccae8e9099bd67840ff8737b043496" dependencies = [ "environmental", "parity-scale-codec", @@ -10174,7 +10305,7 @@ dependencies = [ "parity-scale-codec", "scale-info", "sp-runtime", - "thiserror", + "thiserror 1.0.69", ] [[package]] @@ -10188,7 +10319,7 @@ dependencies = [ "libsecp256k1", "log", "parity-scale-codec", - "polkavm-derive", + "polkavm-derive 0.9.1", "rustversion", "secp256k1", "sp-core", @@ -10229,7 +10360,7 @@ name = "sp-maybe-compressed-blob" version = "11.0.0" source = "git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-stable2409#87971b3e92721bdf10bf40b410eaae779d494ca0" dependencies = [ - "thiserror", + "thiserror 1.0.69", "zstd 0.12.4", ] @@ -10313,13 +10444,13 @@ dependencies = [ [[package]] name = "sp-runtime-interface" version = "24.0.0" -source = "git+https://github.com/paritytech/polkadot-sdk#8614dc0e055d06de4a3774ac1da0a422b33f34e2" +source = "git+https://github.com/paritytech/polkadot-sdk#80e30ec3cdccae8e9099bd67840ff8737b043496" dependencies = [ "bytes", "impl-trait-for-tuples", "parity-scale-codec", - "polkavm-derive", - "primitive-types", + "polkavm-derive 0.18.0", + "primitive-types 0.13.1", "sp-externalities 0.25.0", "sp-runtime-interface-proc-macro 17.0.0", "sp-std 14.0.0 (git+https://github.com/paritytech/polkadot-sdk)", @@ -10337,8 +10468,8 @@ dependencies = [ "bytes", "impl-trait-for-tuples", "parity-scale-codec", - "polkavm-derive", - "primitive-types", + "polkavm-derive 0.9.1", + "primitive-types 0.12.2", "sp-externalities 0.29.0", "sp-runtime-interface-proc-macro 18.0.0", "sp-std 14.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-stable2409)", @@ -10351,14 +10482,14 @@ dependencies = [ [[package]] name = "sp-runtime-interface-proc-macro" version = "17.0.0" -source = "git+https://github.com/paritytech/polkadot-sdk#8614dc0e055d06de4a3774ac1da0a422b33f34e2" +source = "git+https://github.com/paritytech/polkadot-sdk#80e30ec3cdccae8e9099bd67840ff8737b043496" dependencies = [ "Inflector", "expander", "proc-macro-crate 3.2.0", "proc-macro2", "quote", - "syn 2.0.90", + "syn 2.0.96", ] [[package]] @@ -10371,7 +10502,7 @@ dependencies = [ "proc-macro-crate 3.2.0", "proc-macro2", "quote", - "syn 2.0.90", + "syn 2.0.96", ] [[package]] @@ -10416,7 +10547,7 @@ dependencies = [ "sp-externalities 0.29.0", "sp-panic-handler", "sp-trie", - "thiserror", + "thiserror 1.0.69", "tracing", "trie-db", ] @@ -10441,7 +10572,7 @@ dependencies = [ "sp-externalities 0.29.0", "sp-runtime", "sp-runtime-interface 28.0.0", - "thiserror", + "thiserror 1.0.69", "x25519-dalek", ] @@ -10453,14 +10584,14 @@ source = "git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-stable [[package]] name = "sp-std" version = "14.0.0" -source = "git+https://github.com/paritytech/polkadot-sdk#8614dc0e055d06de4a3774ac1da0a422b33f34e2" +source = "git+https://github.com/paritytech/polkadot-sdk#80e30ec3cdccae8e9099bd67840ff8737b043496" [[package]] name = "sp-storage" version = "19.0.0" -source = "git+https://github.com/paritytech/polkadot-sdk#8614dc0e055d06de4a3774ac1da0a422b33f34e2" +source = "git+https://github.com/paritytech/polkadot-sdk#80e30ec3cdccae8e9099bd67840ff8737b043496" dependencies = [ - "impl-serde", + "impl-serde 0.5.0", "parity-scale-codec", "ref-cast", "serde", @@ -10472,7 +10603,7 @@ name = "sp-storage" version = "21.0.0" source = "git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-stable2409#87971b3e92721bdf10bf40b410eaae779d494ca0" dependencies = [ - "impl-serde", + "impl-serde 0.4.0", "parity-scale-codec", "ref-cast", "serde", @@ -10488,18 +10619,18 @@ dependencies = [ "parity-scale-codec", "sp-inherents", "sp-runtime", - "thiserror", + "thiserror 1.0.69", ] [[package]] name = "sp-tracing" version = "16.0.0" -source = "git+https://github.com/paritytech/polkadot-sdk#8614dc0e055d06de4a3774ac1da0a422b33f34e2" +source = "git+https://github.com/paritytech/polkadot-sdk#80e30ec3cdccae8e9099bd67840ff8737b043496" dependencies = [ "parity-scale-codec", "tracing", "tracing-core", - "tracing-subscriber 0.3.18", + "tracing-subscriber 0.3.19", ] [[package]] @@ -10510,7 +10641,7 @@ dependencies = [ "parity-scale-codec", "tracing", "tracing-core", - "tracing-subscriber 0.3.18", + "tracing-subscriber 0.3.19", ] [[package]] @@ -10553,7 +10684,7 @@ dependencies = [ "schnellru", "sp-core", "sp-externalities 0.29.0", - "thiserror", + "thiserror 1.0.69", "tracing", "trie-db", "trie-root", @@ -10564,7 +10695,7 @@ name = "sp-version" version = "37.0.0" source = "git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-stable2409#87971b3e92721bdf10bf40b410eaae779d494ca0" dependencies = [ - "impl-serde", + "impl-serde 0.4.0", "parity-scale-codec", "parity-wasm", "scale-info", @@ -10573,7 +10704,7 @@ dependencies = [ "sp-runtime", "sp-std 14.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-stable2409)", "sp-version-proc-macro", - "thiserror", + "thiserror 1.0.69", ] [[package]] @@ -10584,13 +10715,13 @@ dependencies = [ "parity-scale-codec", "proc-macro2", "quote", - "syn 2.0.90", + "syn 2.0.96", ] [[package]] name = "sp-wasm-interface" version = "20.0.0" -source = "git+https://github.com/paritytech/polkadot-sdk#8614dc0e055d06de4a3774ac1da0a422b33f34e2" +source = "git+https://github.com/paritytech/polkadot-sdk#80e30ec3cdccae8e9099bd67840ff8737b043496" dependencies = [ "anyhow", "impl-trait-for-tuples", @@ -10658,21 +10789,11 @@ dependencies = [ "der", ] -[[package]] -name = "sqlformat" -version = "0.2.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7bba3a93db0cc4f7bdece8bb09e77e2e785c20bfebf79eb8340ed80708048790" -dependencies = [ - "nom", - "unicode_categories", -] - [[package]] name = "sqlx" -version = "0.8.2" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93334716a037193fac19df402f8571269c84a00852f6a7066b5d2616dcd64d3e" +checksum = "4410e73b3c0d8442c5f99b425d7a435b5ee0ae4167b3196771dd3f7a01be745f" dependencies = [ "sqlx-core", "sqlx-macros", @@ -10681,37 +10802,31 @@ dependencies = [ [[package]] name = "sqlx-core" -version = "0.8.2" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d4d8060b456358185f7d50c55d9b5066ad956956fddec42ee2e8567134a8936e" +checksum = "6a007b6936676aa9ab40207cde35daab0a04b823be8ae004368c0793b96a61e0" dependencies = [ - "atoi", - "byteorder", "bytes", "crc", "crossbeam-queue", "either", - "event-listener 5.3.1", - "futures-channel", + "event-listener 5.4.0", "futures-core", "futures-intrusive", "futures-io", "futures-util", - "hashbrown 0.14.5", - "hashlink 0.9.1", - "hex", - "indexmap 2.6.0", + "hashbrown 0.15.2", + "hashlink 0.10.0", + "indexmap 2.7.1", "log", "memchr", "native-tls", "once_cell", - "paste", "percent-encoding", "serde", "sha2 0.10.8", "smallvec", - "sqlformat", - "thiserror", + "thiserror 2.0.11", "tokio", "tokio-stream", "tracing", @@ -10720,22 +10835,22 @@ dependencies = [ [[package]] name = "sqlx-macros" -version = "0.8.2" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cac0692bcc9de3b073e8d747391827297e075c7710ff6276d9f7a1f3d58c6657" +checksum = "3112e2ad78643fef903618d78cf0aec1cb3134b019730edb039b69eaf531f310" dependencies = [ "proc-macro2", "quote", "sqlx-core", "sqlx-macros-core", - "syn 2.0.90", + "syn 2.0.96", ] [[package]] name = "sqlx-macros-core" -version = "0.8.2" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1804e8a7c7865599c9c79be146dc8a9fd8cc86935fa641d3ea58e5f0688abaa5" +checksum = "4e9f90acc5ab146a99bf5061a7eb4976b573f560bc898ef3bf8435448dd5e7ad" dependencies = [ "dotenvy", "either", @@ -10749,7 +10864,7 @@ dependencies = [ "sha2 0.10.8", "sqlx-core", "sqlx-sqlite", - "syn 2.0.90", + "syn 2.0.96", "tempfile", "tokio", "url", @@ -10757,9 +10872,9 @@ dependencies = [ [[package]] name = "sqlx-sqlite" -version = "0.8.2" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d5b2cf34a45953bfd3daaf3db0f7a7878ab9b7a6b91b422d24a7a9e4c857b680" +checksum = "f85ca71d3a5b24e64e1d08dd8fe36c6c95c339a896cc33068148906784620540" dependencies = [ "atoi", "flume", @@ -10780,9 +10895,9 @@ dependencies = [ [[package]] name = "ss58-registry" -version = "1.50.0" +version = "1.51.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43fce22ed1df64d04b262351c8f9d5c6da4f76f79f25ad15529792f893fad25d" +checksum = "19409f13998e55816d1c728395af0b52ec066206341d939e22e7766df9b494b8" dependencies = [ "Inflector", "num-format", @@ -10841,9 +10956,9 @@ dependencies = [ [[package]] name = "static_init_macro" -version = "1.0.2" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70a2595fc3aa78f2d0e45dd425b22282dd863273761cc77780914b2cf3003acf" +checksum = "1389c88ddd739ec6d3f8f83343764a0e944cd23cfbf126a9796a714b0b6edd6f" dependencies = [ "cfg_aliases", "memchr", @@ -10868,7 +10983,7 @@ dependencies = [ "sctp-proto", "serde", "sha-1", - "thiserror", + "thiserror 1.0.69", "tracing", ] @@ -10916,7 +11031,7 @@ dependencies = [ "proc-macro2", "quote", "rustversion", - "syn 2.0.90", + "syn 2.0.96", ] [[package]] @@ -10973,11 +11088,11 @@ version = "0.17.0" source = "git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-stable2409#87971b3e92721bdf10bf40b410eaae779d494ca0" dependencies = [ "http-body-util", - "hyper 1.5.0", + "hyper 1.6.0", "hyper-util", "log", "prometheus", - "thiserror", + "thiserror 1.0.69", "tokio", ] @@ -11020,7 +11135,7 @@ dependencies = [ "quote", "rayon", "subtensor-linting", - "syn 2.0.90", + "syn 2.0.96", "walkdir", ] @@ -11056,7 +11171,7 @@ dependencies = [ "proc-macro2", "procedural-fork", "quote", - "syn 2.0.90", + "syn 2.0.96", ] [[package]] @@ -11066,7 +11181,7 @@ dependencies = [ "ahash 0.8.11", "proc-macro2", "quote", - "syn 2.0.90", + "syn 2.0.96", ] [[package]] @@ -11075,7 +11190,7 @@ version = "0.1.0" dependencies = [ "anyhow", "clap", - "semver 1.0.23", + "semver 1.0.25", "toml_edit", ] @@ -11104,9 +11219,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.90" +version = "2.0.96" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "919d3b74a5dd0ccd15aeb8f93e7006bd9e14c295087c9896a110f490752bcf31" +checksum = "d5d0adab1ae378d7f53bdebc67a39f1f151407ef230f0ce2883572f5d8985c80" dependencies = [ "proc-macro2", "quote", @@ -11133,25 +11248,25 @@ checksum = "c8af7666ab7b6390ab78131fb5b0fce11d6b7a6951602017c35fa82800708971" dependencies = [ "proc-macro2", "quote", - "syn 2.0.90", + "syn 2.0.96", ] [[package]] name = "system-configuration" -version = "0.5.1" +version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba3a3adc5c275d719af8cb4272ea1c4a6d668a777f37e115f6d11ddbc1c8e0e7" +checksum = "3c879d448e9d986b661742763247d3693ed13609438cf3d006f51f5368a5ba6b" dependencies = [ - "bitflags 1.3.2", + "bitflags 2.8.0", "core-foundation", "system-configuration-sys", ] [[package]] name = "system-configuration-sys" -version = "0.5.0" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a75fb188eb626b924683e3b95e3a48e63551fcfb51949de2f06a9d91dbee93c9" +checksum = "8e1d1b10ced5ca923a1fcb8d03e96b8d3268065d724548c0211415ff6ac6bac4" dependencies = [ "core-foundation-sys", "libc", @@ -11171,14 +11286,15 @@ checksum = "61c41af27dd6d1e27b1b16b489db798443478cef1f06a660c96db617ba5de3b1" [[package]] name = "tempfile" -version = "3.13.0" +version = "3.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0f2c9fc62d0beef6951ccffd757e241266a2c833136efbe35af6cd2567dca5b" +checksum = "38c246215d7d24f48ae091a2902398798e05d978b24315d6efbc00ede9a8bb91" dependencies = [ "cfg-if", "fastrand", + "getrandom 0.3.1", "once_cell", - "rustix 0.38.37", + "rustix 0.38.44", "windows-sys 0.59.0", ] @@ -11193,38 +11309,58 @@ dependencies = [ [[package]] name = "terminal_size" -version = "0.4.0" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4f599bd7ca042cfdf8f4512b277c02ba102247820f9d9d4a9f521f496751a6ef" +checksum = "5352447f921fda68cf61b4101566c0bdb5104eff6804d0678e5227580ab6a4e9" dependencies = [ - "rustix 0.38.37", + "rustix 0.38.44", "windows-sys 0.59.0", ] [[package]] name = "termtree" -version = "0.4.1" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3369f5ac52d5eb6ab48c6b4ffdc8efbcad6b89c765749064ba298f2c68a16a76" +checksum = "8f50febec83f5ee1df3015341d8bd429f2d1cc62bcba7ea2076759d315084683" + +[[package]] +name = "thiserror" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" +dependencies = [ + "thiserror-impl 1.0.69", +] [[package]] name = "thiserror" -version = "1.0.64" +version = "2.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d50af8abc119fb8bb6dbabcfa89656f46f84aa0ac7688088608076ad2b459a84" +checksum = "d452f284b73e6d76dd36758a0c8684b1d5be31f92b89d07fd5822175732206fc" dependencies = [ - "thiserror-impl", + "thiserror-impl 2.0.11", ] [[package]] name = "thiserror-impl" -version = "1.0.64" +version = "1.0.69" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08904e7672f5eb876eaaf87e0ce17857500934f4981c4a0ab2b4aa98baac7fc3" +checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" dependencies = [ "proc-macro2", "quote", - "syn 2.0.90", + "syn 2.0.96", +] + +[[package]] +name = "thiserror-impl" +version = "2.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26afc1baea8a989337eeb52b6e72a039780ce45c3edfcc9c5b9d112feeb173c2" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.96", ] [[package]] @@ -11264,9 +11400,9 @@ dependencies = [ [[package]] name = "time" -version = "0.3.36" +version = "0.3.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5dfd88e563464686c916c7e46e623e520ddc6d79fa6641390f2e3fa86e83e885" +checksum = "35e7868883861bd0e56d9ac6efcaaca0d6d5d82a2a7ec8209ff492c07cf37b21" dependencies = [ "deranged", "itoa", @@ -11285,9 +11421,9 @@ checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3" [[package]] name = "time-macros" -version = "0.2.18" +version = "0.2.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f252a68540fde3a3877aeea552b832b40ab9a69e318efd078774a01ddee1ccf" +checksum = "2834e6017e3e5e4b9834939793b282bc03b37a3336245fa820e35e233e2a85de" dependencies = [ "num-conv", "time-core", @@ -11314,9 +11450,9 @@ dependencies = [ [[package]] name = "tinyvec" -version = "1.8.0" +version = "1.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "445e881f4f6d382d5f27c034e25eb92edd7c784ceab92a0937db7f2e9471b938" +checksum = "022db8904dfa342efe721985167e9fcd16c29b226db4397ed752a761cfce81e8" dependencies = [ "tinyvec_macros", ] @@ -11357,9 +11493,9 @@ dependencies = [ [[package]] name = "tokio" -version = "1.40.0" +version = "1.43.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2b070231665d27ad9ec9b8df639893f46727666c6767db40317fbe920a5d998" +checksum = "3d61fa4ffa3de412bfea335c6ecff681de2b609ba3c77ef3e00e521813a9ed9e" dependencies = [ "backtrace", "bytes", @@ -11368,20 +11504,20 @@ dependencies = [ "parking_lot 0.12.3", "pin-project-lite", "signal-hook-registry", - "socket2 0.5.7", + "socket2 0.5.8", "tokio-macros", "windows-sys 0.52.0", ] [[package]] name = "tokio-macros" -version = "2.4.0" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "693d596312e88961bc67d7f1f97af8a70227d9f90c31bba5806eec004978d752" +checksum = "6e06d43f1345a3bcd39f6a56dbb7dcab2ba47e68e8ac134855e7e2bdbaf8cab8" dependencies = [ "proc-macro2", "quote", - "syn 2.0.90", + "syn 2.0.96", ] [[package]] @@ -11396,9 +11532,9 @@ dependencies = [ [[package]] name = "tokio-stream" -version = "0.1.16" +version = "0.1.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4f4e6ce100d0eb49a2734f8c0812bcd324cf357d21810932c5df6b96ef2b86f1" +checksum = "eca58d7bba4a75707817a2c44174253f9236b2d5fbd055602e9d5c07c139a047" dependencies = [ "futures-core", "pin-project-lite", @@ -11423,9 +11559,9 @@ dependencies = [ [[package]] name = "tokio-util" -version = "0.7.12" +version = "0.7.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61e7c3654c13bcd040d4a03abee2c75b1d14a37b423cf5a813ceae1cc903ec6a" +checksum = "d7fcaa8d55a2bdd6b83ace262b016eca0d79ee02818c5c1bcdf0305114081078" dependencies = [ "bytes", "futures-core", @@ -11471,7 +11607,7 @@ version = "0.22.22" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4ae48d6208a266e853d946088ed816055e556cc6028c5e8e2b84d9fa5dd7c7f5" dependencies = [ - "indexmap 2.6.0", + "indexmap 2.7.1", "serde", "serde_spanned", "toml_datetime", @@ -11499,9 +11635,9 @@ version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e9cd434a998747dd2c4276bc96ee2e0c7a2eadf3cae88e52be55a05fa9053f5" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.8.0", "bytes", - "http 1.1.0", + "http 1.2.0", "http-body 1.0.1", "http-body-util", "pin-project-lite", @@ -11523,9 +11659,9 @@ checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3" [[package]] name = "tracing" -version = "0.1.40" +version = "0.1.41" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef" +checksum = "784e0ac535deb450455cbfa28a6f0df145ea1bb7ae51b821cf5e7927fdcfbdd0" dependencies = [ "log", "pin-project-lite", @@ -11535,20 +11671,20 @@ dependencies = [ [[package]] name = "tracing-attributes" -version = "0.1.27" +version = "0.1.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" +checksum = "395ae124c09f9e6918a2310af6038fba074bcf474ac352496d5910dd59a2226d" dependencies = [ "proc-macro2", "quote", - "syn 2.0.90", + "syn 2.0.96", ] [[package]] name = "tracing-core" -version = "0.1.32" +version = "0.1.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54" +checksum = "e672c95779cf947c5311f83787af4fa8fffd12fb27e4993211a84bdfd9610f9c" dependencies = [ "once_cell", "valuable", @@ -11586,9 +11722,9 @@ dependencies = [ [[package]] name = "tracing-subscriber" -version = "0.3.18" +version = "0.3.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad0f048c97dbd9faa9b7df56362b8ebcaa52adb06b498c050d2f4e32f90a7a8b" +checksum = "e8189decb5ac0fa7bc8b96b7cb9b2701d60d48805aca84a238004d665fcc4008" dependencies = [ "matchers", "nu-ansi-term", @@ -11644,7 +11780,7 @@ dependencies = [ "rand", "smallvec", "socket2 0.4.10", - "thiserror", + "thiserror 1.0.69", "tinyvec", "tokio", "tracing", @@ -11669,7 +11805,7 @@ dependencies = [ "once_cell", "rand", "smallvec", - "thiserror", + "thiserror 1.0.69", "tinyvec", "tokio", "tracing", @@ -11691,7 +11827,7 @@ dependencies = [ "rand", "resolv-conf", "smallvec", - "thiserror", + "thiserror 1.0.69", "tokio", "tracing", "trust-dns-proto 0.23.2", @@ -11724,7 +11860,7 @@ dependencies = [ "rand", "rustls 0.21.12", "sha1", - "thiserror", + "thiserror 1.0.69", "url", "utf-8", ] @@ -11774,17 +11910,29 @@ dependencies = [ "static_assertions", ] +[[package]] +name = "uint" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "909988d098b2f738727b161a106cfc7cab00c539c2687a8836f8e565976fb53e" +dependencies = [ + "byteorder", + "crunchy", + "hex", + "static_assertions", +] + [[package]] name = "unicode-bidi" -version = "0.3.17" +version = "0.3.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ab17db44d7388991a428b2ee655ce0c212e862eff1768a455c58f9aad6e7893" +checksum = "5c1cb5db39152898a79168971543b1cb5020dff7fe43c8dc468b0885f5e29df5" [[package]] name = "unicode-ident" -version = "1.0.13" +version = "1.0.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e91b56cd4cadaeb79bbf1a5645f6b4f8dc5bde8834ad5894a8db35fda9efa1fe" +checksum = "a210d160f08b701c8721ba1c726c11662f877ea6b7094007e1ca9a1041945034" [[package]] name = "unicode-normalization" @@ -11802,16 +11950,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7dd6e30e90baa6f72411720665d41d89b9a3d039dc45b8faea1ddd07f617f6af" [[package]] -name = "unicode-xid" -version = "0.2.6" +name = "unicode-width" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" +checksum = "1fc81956842c57dac11422a97c3b8195a1ff727f06e85c84ed2e8aa277c9a0fd" [[package]] -name = "unicode_categories" -version = "0.1.1" +name = "unicode-xid" +version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "39ec24b3121d976906ece63c9daad25b85969647682eee313cb5779fdd69e14e" +checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" [[package]] name = "universal-hash" @@ -11894,9 +12042,9 @@ checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" [[package]] name = "valuable" -version = "0.1.0" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" +checksum = "ba73ea9cf16a25df0c8caa16c51acb937d5712a8429db78a3ee29d5dcacd3a65" [[package]] name = "vcpkg" @@ -11936,7 +12084,7 @@ dependencies = [ "rand_core", "sha2 0.10.8", "sha3", - "thiserror", + "thiserror 1.0.69", "zeroize", ] @@ -11965,49 +12113,59 @@ version = "0.11.0+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" +[[package]] +name = "wasi" +version = "0.13.3+wasi-0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26816d2e1a4a36a2940b96c5296ce403917633dff8f3440e9b236ed6f6bacad2" +dependencies = [ + "wit-bindgen-rt", +] + [[package]] name = "wasm-bindgen" -version = "0.2.93" +version = "0.2.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a82edfc16a6c469f5f44dc7b571814045d60404b55a0ee849f9bcfa2e63dd9b5" +checksum = "1edc8929d7499fc4e8f0be2262a241556cfc54a0bea223790e71446f2aab1ef5" dependencies = [ "cfg-if", "once_cell", + "rustversion", "wasm-bindgen-macro", ] [[package]] name = "wasm-bindgen-backend" -version = "0.2.93" +version = "0.2.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9de396da306523044d3302746f1208fa71d7532227f15e347e2d93e4145dd77b" +checksum = "2f0a0651a5c2bc21487bde11ee802ccaf4c51935d0d3d42a6101f98161700bc6" dependencies = [ "bumpalo", "log", - "once_cell", "proc-macro2", "quote", - "syn 2.0.90", + "syn 2.0.96", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-futures" -version = "0.4.43" +version = "0.4.50" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61e9300f63a621e96ed275155c108eb6f843b6a26d053f122ab69724559dc8ed" +checksum = "555d470ec0bc3bb57890405e5d4322cc9ea83cebb085523ced7be4144dac1e61" dependencies = [ "cfg-if", "js-sys", + "once_cell", "wasm-bindgen", "web-sys", ] [[package]] name = "wasm-bindgen-macro" -version = "0.2.93" +version = "0.2.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "585c4c91a46b072c92e908d99cb1dcdf95c5218eeb6f3bf1efa991ee7a68cccf" +checksum = "7fe63fc6d09ed3792bd0897b314f53de8e16568c2b3f7982f468c0bf9bd0b407" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -12015,22 +12173,25 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.93" +version = "0.2.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "afc340c74d9005395cf9dd098506f7f44e38f2b4a21c6aaacf9a105ea5e1e836" +checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de" dependencies = [ "proc-macro2", "quote", - "syn 2.0.90", + "syn 2.0.96", "wasm-bindgen-backend", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" -version = "0.2.93" +version = "0.2.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c62a0a307cb4a311d3a07867860911ca130c3494e8c2719593806c08bc5d0484" +checksum = "1a05d73b933a847d6cccdda8f838a22ff101ad9bf93e33684f39c1f5f0eece3d" +dependencies = [ + "unicode-ident", +] [[package]] name = "wasm-instrument" @@ -12052,7 +12213,7 @@ dependencies = [ "strum 0.24.1", "strum_macros 0.24.3", "tempfile", - "thiserror", + "thiserror 1.0.69", "wasm-opt-cxx-sys", "wasm-opt-sys", ] @@ -12179,7 +12340,7 @@ dependencies = [ "log", "object 0.30.4", "target-lexicon", - "thiserror", + "thiserror 1.0.69", "wasmparser", "wasmtime-cranelift-shared", "wasmtime-environ", @@ -12214,7 +12375,7 @@ dependencies = [ "object 0.30.4", "serde", "target-lexicon", - "thiserror", + "thiserror 1.0.69", "wasmparser", "wasmtime-types", ] @@ -12297,15 +12458,15 @@ checksum = "a4f6fffd2a1011887d57f07654dd112791e872e3ff4a2e626aee8059ee17f06f" dependencies = [ "cranelift-entity", "serde", - "thiserror", + "thiserror 1.0.69", "wasmparser", ] [[package]] name = "web-sys" -version = "0.3.70" +version = "0.3.77" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26fdeaafd9bd129f65e7c031593c24d62186301e0c72c8978fa1678be7d532c0" +checksum = "33b6dd2ef9186f1f2072e409e99cd22a975331a6b3591b12c764e0e55c60d5d2" dependencies = [ "js-sys", "wasm-bindgen", @@ -12336,14 +12497,14 @@ dependencies = [ "either", "home", "once_cell", - "rustix 0.38.37", + "rustix 0.38.44", ] [[package]] name = "wide" -version = "0.7.28" +version = "0.7.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b828f995bf1e9622031f8009f8481a85406ce1f4d4588ff746d872043e855690" +checksum = "41b5576b9a81633f3e8df296ce0063042a73507636cbe956c61133dd7034ab22" dependencies = [ "bytemuck", "safe_arch", @@ -12388,28 +12549,38 @@ checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" [[package]] name = "windows" -version = "0.51.1" +version = "0.53.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca229916c5ee38c2f2bc1e9d8f04df975b4bd93f9955dc69fabb5d91270045c9" +checksum = "efc5cf48f83140dcaab716eeaea345f9e93d0018fb81162753a3f76c3397b538" dependencies = [ - "windows-core 0.51.1", - "windows-targets 0.48.5", + "windows-core 0.53.0", + "windows-targets 0.52.6", ] [[package]] name = "windows-core" -version = "0.51.1" +version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1f8cf84f35d2db49a46868f947758c7a1138116f7fac3bc844f43ade1292e64" +checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9" dependencies = [ - "windows-targets 0.48.5", + "windows-targets 0.52.6", ] [[package]] name = "windows-core" -version = "0.52.0" +version = "0.53.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9" +checksum = "9dcc5b895a6377f1ab9fa55acedab1fd5ac0db66ad1e6c7f47e28a22e446a5dd" +dependencies = [ + "windows-result", + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-result" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e383302e8ec8515204254685643de10811af0ed97ea37210dc26fb0032647f8" dependencies = [ "windows-targets 0.52.6", ] @@ -12645,9 +12816,9 @@ checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" [[package]] name = "winnow" -version = "0.6.20" +version = "0.6.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "36c1fec1a2bb5866f07c25f68c26e565c4c200aebb96d7e55710c19d3e8ac49b" +checksum = "ad699df48212c6cc6eb4435f35500ac6fd3b9913324f938aea302022ce19d310" dependencies = [ "memchr", ] @@ -12662,6 +12833,15 @@ dependencies = [ "windows-sys 0.48.0", ] +[[package]] +name = "wit-bindgen-rt" +version = "0.33.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3268f3d866458b787f390cf61f4bbb563b922d091359f9608842999eaee3943c" +dependencies = [ + "bitflags 2.8.0", +] + [[package]] name = "write16" version = "1.0.0" @@ -12708,7 +12888,7 @@ dependencies = [ "nom", "oid-registry 0.6.1", "rusticata-macros", - "thiserror", + "thiserror 1.0.69", "time", ] @@ -12725,7 +12905,7 @@ dependencies = [ "nom", "oid-registry 0.7.1", "rusticata-macros", - "thiserror", + "thiserror 1.0.69", "time", ] @@ -12737,14 +12917,14 @@ dependencies = [ "Inflector", "proc-macro2", "quote", - "syn 2.0.90", + "syn 2.0.96", ] [[package]] name = "xml-rs" -version = "0.8.22" +version = "0.8.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af4e2e2f7cba5a093896c1e150fbfe177d1883e7448200efb81d40b9d339ef26" +checksum = "c5b940ebc25896e71dd073bad2dbaa2abfe97b0a391415e22ad1326d9c54e3c4" [[package]] name = "xmltree" @@ -12799,7 +12979,7 @@ checksum = "2380878cad4ac9aac1e2435f3eb4020e8374b5f13c296cb75b4620ff8e229154" dependencies = [ "proc-macro2", "quote", - "syn 2.0.90", + "syn 2.0.96", "synstructure 0.13.1", ] @@ -12821,7 +13001,7 @@ checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.90", + "syn 2.0.96", ] [[package]] @@ -12841,7 +13021,7 @@ checksum = "595eed982f7d355beb85837f651fa22e90b3c044842dc7f2c2842c086f295808" dependencies = [ "proc-macro2", "quote", - "syn 2.0.90", + "syn 2.0.96", "synstructure 0.13.1", ] @@ -12862,7 +13042,7 @@ checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" dependencies = [ "proc-macro2", "quote", - "syn 2.0.90", + "syn 2.0.96", ] [[package]] @@ -12884,7 +13064,7 @@ checksum = "6eafa6dfb17584ea3e2bd6e76e0cc15ad7af12b09abdd1ca55961bed9b1063c6" dependencies = [ "proc-macro2", "quote", - "syn 2.0.90", + "syn 2.0.96", ] [[package]] From f25cabc641df8f379ac3a2d6d0030f42477f9df0 Mon Sep 17 00:00:00 2001 From: open-junius <zhou@opentensor.dev> Date: Thu, 30 Jan 2025 18:22:38 +0800 Subject: [PATCH 137/145] add len check --- runtime/src/precompiles/subnet.rs | 59 +++++++++++++++++++++++++++++-- 1 file changed, 57 insertions(+), 2 deletions(-) diff --git a/runtime/src/precompiles/subnet.rs b/runtime/src/precompiles/subnet.rs index edef73ff0..36f861d48 100644 --- a/runtime/src/precompiles/subnet.rs +++ b/runtime/src/precompiles/subnet.rs @@ -7,8 +7,10 @@ use sp_runtime::AccountId32; use sp_std::vec; pub const SUBNET_PRECOMPILE_INDEX: u64 = 2051; +// bytes with max lenght 1K +pub const MAX_SINGLE_PARAMETER_SIZE: usize = 1024; // three bytes with max lenght 1K -pub const MAX_PARAMETER_SIZE: usize = 3 * 1024; +pub const MAX_PARAMETER_SIZE: usize = 3 * MAX_SINGLE_PARAMETER_SIZE; // ss58 public key i.e., the contract sends funds it received to the destination address from the // method parameter. @@ -90,18 +92,46 @@ impl SubnetPrecompile { fn parse_register_network_parameters( data: &[u8], ) -> Result<(AccountId32, vec::Vec<u8>, vec::Vec<u8>, vec::Vec<u8>), PrecompileFailure> { - let (pubkey, _) = get_pubkey(data)?; + let (pubkey, dynamic_params) = get_pubkey(data)?; + let dynamic_data_len = dynamic_params.len(); let mut buf = [0_u8; 4]; // get all start point for three data items: name, repo and contact buf.copy_from_slice(get_slice(data, 60, 64)?); let subnet_name_start: usize = u32::from_be_bytes(buf) as usize; + if subnet_name_start > dynamic_data_len { + log::error!( + "the start position of subnet name as {} is too big ", + subnet_name_start + ); + return Err(PrecompileFailure::Error { + exit_status: ExitError::InvalidRange, + }); + } buf.copy_from_slice(get_slice(data, 92, 96)?); let github_repo_start: usize = u32::from_be_bytes(buf) as usize; + if github_repo_start > dynamic_data_len { + log::error!( + "the start position of github repo as {} is too big ", + github_repo_start + ); + return Err(PrecompileFailure::Error { + exit_status: ExitError::InvalidRange, + }); + } buf.copy_from_slice(get_slice(data, 124, 128)?); let subnet_contact_start: usize = u32::from_be_bytes(buf) as usize; + if subnet_contact_start > dynamic_data_len { + log::error!( + "the start position of subnet contact as {} is too big ", + subnet_contact_start + ); + return Err(PrecompileFailure::Error { + exit_status: ExitError::InvalidRange, + }); + } // get name buf.copy_from_slice(get_slice( @@ -111,6 +141,13 @@ impl SubnetPrecompile { )?); let subnet_name_len: usize = u32::from_be_bytes(buf) as usize; + if subnet_name_len > MAX_SINGLE_PARAMETER_SIZE { + log::error!("the length of subnet nae as {} is too big", subnet_name_len); + return Err(PrecompileFailure::Error { + exit_status: ExitError::InvalidRange, + }); + } + let mut name_vec = vec![0; subnet_name_len]; name_vec.copy_from_slice(get_slice( data, @@ -125,6 +162,15 @@ impl SubnetPrecompile { github_repo_start + 32, )?); let github_repo_len: usize = u32::from_be_bytes(buf) as usize; + if github_repo_len > MAX_SINGLE_PARAMETER_SIZE { + log::error!( + "the length of github repo as {} is too big", + github_repo_len + ); + return Err(PrecompileFailure::Error { + exit_status: ExitError::InvalidRange, + }); + } let mut repo_vec = vec![0; github_repo_len]; repo_vec.copy_from_slice(get_slice( @@ -140,6 +186,15 @@ impl SubnetPrecompile { subnet_contact_start + 32, )?); let subnet_contact_len: usize = u32::from_be_bytes(buf) as usize; + if subnet_contact_len > MAX_SINGLE_PARAMETER_SIZE { + log::error!( + "the length of subnet contact as {} is too big", + subnet_contact_len + ); + return Err(PrecompileFailure::Error { + exit_status: ExitError::InvalidRange, + }); + } let mut contact_vec = vec![0; subnet_contact_len]; contact_vec.copy_from_slice(get_slice( From f8d5d4d7e2dfe8745e6445432c3877e976db059e Mon Sep 17 00:00:00 2001 From: Aliaksandr Tsurko <ales@opentensor.dev> Date: Thu, 30 Jan 2025 14:28:17 +0100 Subject: [PATCH 138/145] Fix rao migration did not update TotalIssuance --- .../migrations/migrate_init_total_issuance.rs | 77 ++++++++++--------- .../subtensor/src/migrations/migrate_rao.rs | 16 ++-- scripts/try-runtime-upgrade.sh | 2 +- 3 files changed, 52 insertions(+), 43 deletions(-) diff --git a/pallets/subtensor/src/migrations/migrate_init_total_issuance.rs b/pallets/subtensor/src/migrations/migrate_init_total_issuance.rs index a488771c5..4501ccf6d 100644 --- a/pallets/subtensor/src/migrations/migrate_init_total_issuance.rs +++ b/pallets/subtensor/src/migrations/migrate_init_total_issuance.rs @@ -1,6 +1,6 @@ use super::*; -use frame_support::pallet_prelude::OptionQuery; -use frame_support::{pallet_prelude::Identity, storage_alias}; +use frame_support::pallet_prelude::{Identity, OptionQuery, Weight}; +use frame_support::storage_alias; use sp_std::vec::Vec; // TODO: Implement comprehensive tests for this migration @@ -14,6 +14,43 @@ pub mod deprecated_loaded_emission_format { StorageMap<Pallet<T>, Identity, u16, Vec<(AccountIdOf<T>, u64)>, OptionQuery>; } +pub(crate) fn migrate_init_total_issuance<T: Config>() -> Weight { + // Calculate the total locked tokens across all subnets + let subnets_len = crate::SubnetLocked::<T>::iter().count() as u64; + let total_subnet_locked: u64 = + crate::SubnetLocked::<T>::iter().fold(0, |acc, (_, v)| acc.saturating_add(v)); + + // Retrieve the total balance of all accounts + let total_account_balances = <<T as crate::Config>::Currency as fungible::Inspect< + <T as frame_system::Config>::AccountId, + >>::total_issuance(); + + // Get the total stake from the system + let total_stake = crate::TotalStake::<T>::get(); + + // Retrieve the previous total issuance for logging purposes + let prev_total_issuance = crate::TotalIssuance::<T>::get(); + + // Calculate the new total issuance + let new_total_issuance = total_account_balances + .saturating_add(total_stake) + .saturating_add(total_subnet_locked); + + // Update the total issuance in storage + crate::TotalIssuance::<T>::put(new_total_issuance); + + // Log the change in total issuance + log::info!( + "Subtensor Pallet Total Issuance Updated: previous: {:?}, new: {:?}", + prev_total_issuance, + new_total_issuance + ); + + // Return the weight of the operation + // We performed subnets_len + 5 reads and 1 write + <T as frame_system::Config>::DbWeight::get().reads_writes(subnets_len.saturating_add(5), 1) +} + pub mod initialise_total_issuance { use frame_support::pallet_prelude::Weight; use frame_support::traits::{fungible, OnRuntimeUpgrade}; @@ -33,41 +70,7 @@ pub mod initialise_total_issuance { /// /// Returns the weight of the migration operation. fn on_runtime_upgrade() -> Weight { - // Calculate the total locked tokens across all subnets - let subnets_len = crate::SubnetLocked::<T>::iter().count() as u64; - let total_subnet_locked: u64 = - crate::SubnetLocked::<T>::iter().fold(0, |acc, (_, v)| acc.saturating_add(v)); - - // Retrieve the total balance of all accounts - let total_account_balances = <<T as crate::Config>::Currency as fungible::Inspect< - <T as frame_system::Config>::AccountId, - >>::total_issuance(); - - // Get the total stake from the system - let total_stake = crate::TotalStake::<T>::get(); - - // Retrieve the previous total issuance for logging purposes - let prev_total_issuance = crate::TotalIssuance::<T>::get(); - - // Calculate the new total issuance - let new_total_issuance = total_account_balances - .saturating_add(total_stake) - .saturating_add(total_subnet_locked); - - // Update the total issuance in storage - crate::TotalIssuance::<T>::put(new_total_issuance); - - // Log the change in total issuance - log::info!( - "Subtensor Pallet Total Issuance Updated: previous: {:?}, new: {:?}", - prev_total_issuance, - new_total_issuance - ); - - // Return the weight of the operation - // We performed subnets_len + 5 reads and 1 write - <T as frame_system::Config>::DbWeight::get() - .reads_writes(subnets_len.saturating_add(5), 1) + super::migrate_init_total_issuance::<T>() } /// Performs post-upgrade checks to ensure the migration was successful. diff --git a/pallets/subtensor/src/migrations/migrate_rao.rs b/pallets/subtensor/src/migrations/migrate_rao.rs index 3c034b7da..136bd4c59 100644 --- a/pallets/subtensor/src/migrations/migrate_rao.rs +++ b/pallets/subtensor/src/migrations/migrate_rao.rs @@ -1,11 +1,13 @@ -use super::*; use alloc::string::String; + use frame_support::IterableStorageMap; use frame_support::{traits::Get, weights::Weight}; -use log; use sp_runtime::format; use substrate_fixed::types::U64F64; +use super::*; +use crate::subnets::subnet::POOL_INITIAL_TAO; + pub fn migrate_rao<T: Config>() -> Weight { let migration_name = b"migrate_rao".to_vec(); @@ -69,12 +71,12 @@ pub fn migrate_rao<T: Config>() -> Weight { TokenSymbol::<T>::insert(netuid, Pallet::<T>::get_symbol_for_subnet(0)); continue; } - let owner: T::AccountId = SubnetOwner::<T>::get(netuid); - let lock: u64 = SubnetLocked::<T>::get(netuid); + let owner = SubnetOwner::<T>::get(netuid); + let lock = SubnetLocked::<T>::get(netuid); // Put initial TAO from lock into subnet TAO and produce numerically equal amount of Alpha // The initial TAO is the locked amount, with a minimum of 1 RAO and a cap of 100 TAO. - let pool_initial_tao = 100_000_000_000.min(lock.max(1)); + let pool_initial_tao = POOL_INITIAL_TAO.min(lock.max(1)); let remaining_lock = lock.saturating_sub(pool_initial_tao); // Refund the owner for the remaining lock. @@ -127,6 +129,10 @@ pub fn migrate_rao<T: Config>() -> Weight { // TargetStakesPerInterval::<T>::put(10); (DEPRECATED) } + // update `TotalIssuance`, because currency issuance (`T::Currency`) has changed due to lock + // refunds above + weight = weight.saturating_add(migrate_init_total_issuance::migrate_init_total_issuance::<T>()); + // Mark the migration as completed HasMigrationRun::<T>::insert(&migration_name, true); weight = weight.saturating_add(T::DbWeight::get().writes(1)); diff --git a/scripts/try-runtime-upgrade.sh b/scripts/try-runtime-upgrade.sh index 80495bcb1..bf8ac8393 100755 --- a/scripts/try-runtime-upgrade.sh +++ b/scripts/try-runtime-upgrade.sh @@ -49,7 +49,7 @@ do_try_runtime() { fi eval "try-runtime --runtime $runtime_wasm_path on-runtime-upgrade \ - --no-weight-warnings --disable-spec-version-check --disable-idempotency-checks \ + --no-weight-warnings --disable-spec-version-check --disable-idempotency-checks --checks=all \ $chain_state" } From a7fffd8dec7f59281003b159fd6fa66a207444b3 Mon Sep 17 00:00:00 2001 From: Aliaksandr Tsurko <ales@opentensor.dev> Date: Thu, 30 Jan 2025 15:55:00 +0100 Subject: [PATCH 139/145] Refactor TryState --- pallets/subtensor/src/macros/hooks.rs | 7 +- .../migrations/migrate_init_total_issuance.rs | 7 +- pallets/subtensor/src/utils/mod.rs | 1 + pallets/subtensor/src/utils/try_state.rs | 71 ++++++++++--------- scripts/try-runtime-upgrade.sh | 2 +- 5 files changed, 48 insertions(+), 40 deletions(-) diff --git a/pallets/subtensor/src/macros/hooks.rs b/pallets/subtensor/src/macros/hooks.rs index 2a43238ab..cefc2e24b 100644 --- a/pallets/subtensor/src/macros/hooks.rs +++ b/pallets/subtensor/src/macros/hooks.rs @@ -4,6 +4,9 @@ use frame_support::pallet_macros::pallet_section; /// This can later be imported into the pallet using [`import_section`]. #[pallet_section] mod hooks { + #[cfg(feature = "try-runtime")] + use crate::utils::try_state::TryState; + // ================ // ==== Hooks ===== // ================ @@ -82,7 +85,9 @@ mod hooks { #[cfg(feature = "try-runtime")] fn try_state(_n: BlockNumberFor<T>) -> Result<(), sp_runtime::TryRuntimeError> { - Self::check_accounting_invariants()?; + TryState::<T>::check_total_issuance()?; + // Disabled: https://github.com/opentensor/subtensor/pull/1166 + // TryState::<T>::check_total_stake()?; Ok(()) } } diff --git a/pallets/subtensor/src/migrations/migrate_init_total_issuance.rs b/pallets/subtensor/src/migrations/migrate_init_total_issuance.rs index 4501ccf6d..e1423a625 100644 --- a/pallets/subtensor/src/migrations/migrate_init_total_issuance.rs +++ b/pallets/subtensor/src/migrations/migrate_init_total_issuance.rs @@ -53,9 +53,10 @@ pub(crate) fn migrate_init_total_issuance<T: Config>() -> Weight { pub mod initialise_total_issuance { use frame_support::pallet_prelude::Weight; - use frame_support::traits::{fungible, OnRuntimeUpgrade}; - use sp_core::Get; + use frame_support::traits::OnRuntimeUpgrade; + #[cfg(feature = "try-runtime")] + use crate::utils::try_state::TryState; use crate::*; pub struct Migration<T: Config>(PhantomData<T>); @@ -79,7 +80,7 @@ pub mod initialise_total_issuance { #[cfg(feature = "try-runtime")] fn post_upgrade(_state: Vec<u8>) -> Result<(), sp_runtime::TryRuntimeError> { // Verify that all accounting invariants are satisfied after the migration - crate::Pallet::<T>::check_accounting_invariants()?; + TryState::<T>::check_total_issuance()?; Ok(()) } } diff --git a/pallets/subtensor/src/utils/mod.rs b/pallets/subtensor/src/utils/mod.rs index a42c91119..909ad8959 100644 --- a/pallets/subtensor/src/utils/mod.rs +++ b/pallets/subtensor/src/utils/mod.rs @@ -2,4 +2,5 @@ use super::*; pub mod identity; pub mod misc; pub mod rate_limiting; +#[cfg(feature = "try-runtime")] pub mod try_state; diff --git a/pallets/subtensor/src/utils/try_state.rs b/pallets/subtensor/src/utils/try_state.rs index c2292eeee..9899131c3 100644 --- a/pallets/subtensor/src/utils/try_state.rs +++ b/pallets/subtensor/src/utils/try_state.rs @@ -1,42 +1,17 @@ -use super::*; - -impl<T: Config> Pallet<T> { - /// Checks if the accounting invariants for [`TotalStake`], [`TotalSubnetLocked`], and [`TotalIssuance`] are correct. - /// - /// This function verifies that: - /// 1. The sum of all stakes matches the [`TotalStake`]. - /// 2. The [`TotalSubnetLocked`] is correctly calculated. - /// 3. The [`TotalIssuance`] equals the sum of currency issuance, total stake, and total subnet locked. - /// - /// # Returns - /// - /// Returns `Ok(())` if all invariants are correct, otherwise returns an error. - #[cfg(feature = "try-runtime")] - pub fn check_accounting_invariants() -> Result<(), sp_runtime::TryRuntimeError> { +use core::marker::PhantomData; use frame_support::traits::fungible::Inspect; - // Disabled: https://github.com/opentensor/subtensor/pull/1166 - // - // // Calculate the total staked amount - // let total_staked = SubnetTAO::<T>::iter().fold(0u64, |acc, (netuid, stake)| { - // let acc = acc.saturating_add(stake); - - // if netuid == Self::get_root_netuid() { - // // root network doesn't have initial pool TAO - // acc - // } else { - // acc.saturating_sub(POOL_INITIAL_TAO) - // } - // }); +use crate::subnets::subnet::POOL_INITIAL_TAO; +use super::*; - // // Verify that the calculated total stake matches the stored TotalStake - // ensure!( - // total_staked == TotalStake::<T>::get(), - // "TotalStake does not match total staked", - // ); +pub(crate) struct TryState<T: Config>(PhantomData<T>); +impl<T: Config> TryState<T> { + /// Checks [`TotalIssuance`] equals the sum of currency issuance, total stake, and total subnet + /// locked. + pub(crate) fn check_total_issuance() -> Result<(), sp_runtime::TryRuntimeError> { // Get the total subnet locked amount - let total_subnet_locked = Self::get_total_subnet_locked(); + let total_subnet_locked = Pallet::<T>::get_total_subnet_locked(); // Get the total currency issuance let currency_issuance = T::Currency::total_issuance(); @@ -59,11 +34,37 @@ impl<T: Config> Pallet<T> { expected_total_issuance.checked_sub(total_issuance) } .expect("LHS > RHS"); + ensure!( diff <= delta, "TotalIssuance diff greater than allowable delta", ); - Ok(()) + Ok(()) } + + /// Checks the sum of all stakes matches the [`TotalStake`]. + pub(crate) fn check_total_stake() -> Result<(), sp_runtime::TryRuntimeError> { + // Calculate the total staked amount + let total_staked = SubnetTAO::<T>::iter().fold(0u64, |acc, (netuid, stake)| { + let acc = acc.saturating_add(stake); + + if netuid == Pallet::<T>::get_root_netuid() { + // root network doesn't have initial pool TAO + acc + } else { + acc.saturating_sub(POOL_INITIAL_TAO) + } + }); + + log::warn!("total_staked: {}, TotalStake: {}", total_staked, TotalStake::<T>::get()); + + // Verify that the calculated total stake matches the stored TotalStake + ensure!( + total_staked == TotalStake::<T>::get(), + "TotalStake does not match total staked", + ); + + Ok(()) + } } diff --git a/scripts/try-runtime-upgrade.sh b/scripts/try-runtime-upgrade.sh index bf8ac8393..11ce78147 100755 --- a/scripts/try-runtime-upgrade.sh +++ b/scripts/try-runtime-upgrade.sh @@ -49,7 +49,7 @@ do_try_runtime() { fi eval "try-runtime --runtime $runtime_wasm_path on-runtime-upgrade \ - --no-weight-warnings --disable-spec-version-check --disable-idempotency-checks --checks=all \ + --disable-spec-version-check --disable-idempotency-checks --checks=all \ $chain_state" } From d39a6ea31d13eb4a64875ccd61ff3fa5a3fa948f Mon Sep 17 00:00:00 2001 From: Aliaksandr Tsurko <ales@opentensor.dev> Date: Thu, 30 Jan 2025 15:56:44 +0100 Subject: [PATCH 140/145] Reformat --- pallets/subtensor/src/macros/hooks.rs | 9 ++---- .../migrations/migrate_init_total_issuance.rs | 4 +-- pallets/subtensor/src/utils/try_state.rs | 32 ++++++++++--------- scripts/try-runtime-upgrade.sh | 2 +- 4 files changed, 22 insertions(+), 25 deletions(-) diff --git a/pallets/subtensor/src/macros/hooks.rs b/pallets/subtensor/src/macros/hooks.rs index cefc2e24b..4310d3b1e 100644 --- a/pallets/subtensor/src/macros/hooks.rs +++ b/pallets/subtensor/src/macros/hooks.rs @@ -4,9 +4,6 @@ use frame_support::pallet_macros::pallet_section; /// This can later be imported into the pallet using [`import_section`]. #[pallet_section] mod hooks { - #[cfg(feature = "try-runtime")] - use crate::utils::try_state::TryState; - // ================ // ==== Hooks ===== // ================ @@ -85,9 +82,9 @@ mod hooks { #[cfg(feature = "try-runtime")] fn try_state(_n: BlockNumberFor<T>) -> Result<(), sp_runtime::TryRuntimeError> { - TryState::<T>::check_total_issuance()?; - // Disabled: https://github.com/opentensor/subtensor/pull/1166 - // TryState::<T>::check_total_stake()?; + Self::check_total_issuance()?; + // Disabled: https://github.com/opentensor/subtensor/pull/1166 + // Self::check_total_stake()?; Ok(()) } } diff --git a/pallets/subtensor/src/migrations/migrate_init_total_issuance.rs b/pallets/subtensor/src/migrations/migrate_init_total_issuance.rs index e1423a625..ba9d85bad 100644 --- a/pallets/subtensor/src/migrations/migrate_init_total_issuance.rs +++ b/pallets/subtensor/src/migrations/migrate_init_total_issuance.rs @@ -55,8 +55,6 @@ pub mod initialise_total_issuance { use frame_support::pallet_prelude::Weight; use frame_support::traits::OnRuntimeUpgrade; - #[cfg(feature = "try-runtime")] - use crate::utils::try_state::TryState; use crate::*; pub struct Migration<T: Config>(PhantomData<T>); @@ -80,7 +78,7 @@ pub mod initialise_total_issuance { #[cfg(feature = "try-runtime")] fn post_upgrade(_state: Vec<u8>) -> Result<(), sp_runtime::TryRuntimeError> { // Verify that all accounting invariants are satisfied after the migration - TryState::<T>::check_total_issuance()?; + crate::Pallet::<T>::check_total_issuance()?; Ok(()) } } diff --git a/pallets/subtensor/src/utils/try_state.rs b/pallets/subtensor/src/utils/try_state.rs index 9899131c3..db7e4352e 100644 --- a/pallets/subtensor/src/utils/try_state.rs +++ b/pallets/subtensor/src/utils/try_state.rs @@ -1,17 +1,14 @@ -use core::marker::PhantomData; - use frame_support::traits::fungible::Inspect; +use frame_support::traits::fungible::Inspect; -use crate::subnets::subnet::POOL_INITIAL_TAO; use super::*; +use crate::subnets::subnet::POOL_INITIAL_TAO; -pub(crate) struct TryState<T: Config>(PhantomData<T>); - -impl<T: Config> TryState<T> { - /// Checks [`TotalIssuance`] equals the sum of currency issuance, total stake, and total subnet - /// locked. +impl<T: Config> Pallet<T> { + /// Checks [`TotalIssuance`] equals the sum of currency issuance, total stake, and total subnet + /// locked. pub(crate) fn check_total_issuance() -> Result<(), sp_runtime::TryRuntimeError> { // Get the total subnet locked amount - let total_subnet_locked = Pallet::<T>::get_total_subnet_locked(); + let total_subnet_locked = Self::get_total_subnet_locked(); // Get the total currency issuance let currency_issuance = T::Currency::total_issuance(); @@ -40,16 +37,17 @@ impl<T: Config> TryState<T> { "TotalIssuance diff greater than allowable delta", ); - Ok(()) + Ok(()) } - /// Checks the sum of all stakes matches the [`TotalStake`]. + /// Checks the sum of all stakes matches the [`TotalStake`]. + #[allow(dead_code)] pub(crate) fn check_total_stake() -> Result<(), sp_runtime::TryRuntimeError> { // Calculate the total staked amount let total_staked = SubnetTAO::<T>::iter().fold(0u64, |acc, (netuid, stake)| { let acc = acc.saturating_add(stake); - if netuid == Pallet::<T>::get_root_netuid() { + if netuid == Self::get_root_netuid() { // root network doesn't have initial pool TAO acc } else { @@ -57,7 +55,11 @@ impl<T: Config> TryState<T> { } }); - log::warn!("total_staked: {}, TotalStake: {}", total_staked, TotalStake::<T>::get()); + log::warn!( + "total_staked: {}, TotalStake: {}", + total_staked, + TotalStake::<T>::get() + ); // Verify that the calculated total stake matches the stored TotalStake ensure!( @@ -65,6 +67,6 @@ impl<T: Config> TryState<T> { "TotalStake does not match total staked", ); - Ok(()) - } + Ok(()) + } } diff --git a/scripts/try-runtime-upgrade.sh b/scripts/try-runtime-upgrade.sh index 11ce78147..bf8ac8393 100755 --- a/scripts/try-runtime-upgrade.sh +++ b/scripts/try-runtime-upgrade.sh @@ -49,7 +49,7 @@ do_try_runtime() { fi eval "try-runtime --runtime $runtime_wasm_path on-runtime-upgrade \ - --disable-spec-version-check --disable-idempotency-checks --checks=all \ + --no-weight-warnings --disable-spec-version-check --disable-idempotency-checks --checks=all \ $chain_state" } From 998867ead0e6618fbccbd8bb3d69b0671c2fc02e Mon Sep 17 00:00:00 2001 From: open-junius <zhou@opentensor.dev> Date: Fri, 31 Jan 2025 00:11:02 +0800 Subject: [PATCH 141/145] fix wrong origin --- runtime/src/precompiles/staking.rs | 81 ++++++++++++++++++++++-------- runtime/src/precompiles/subnet.rs | 20 +++++--- 2 files changed, 73 insertions(+), 28 deletions(-) diff --git a/runtime/src/precompiles/staking.rs b/runtime/src/precompiles/staking.rs index 3c3ee7ad9..eefde3a37 100644 --- a/runtime/src/precompiles/staking.rs +++ b/runtime/src/precompiles/staking.rs @@ -26,16 +26,17 @@ // use crate::precompiles::{ - contract_to_origin, get_method_id, get_pubkey, get_slice, parse_netuid, - try_dispatch_runtime_call, + get_method_id, get_pubkey, get_slice, parse_netuid, try_dispatch_runtime_call, }; use crate::{ProxyType, Runtime, RuntimeCall}; +use frame_system::RawOrigin; use pallet_evm::{ - BalanceConverter, ExitError, ExitSucceed, PrecompileFailure, PrecompileHandle, - PrecompileOutput, PrecompileResult, + AddressMapping, BalanceConverter, ExitError, ExitSucceed, HashedAddressMapping, + PrecompileFailure, PrecompileHandle, PrecompileOutput, PrecompileResult, }; use sp_core::U256; -use sp_runtime::traits::{StaticLookup, UniqueSaturatedInto}; +use sp_runtime::traits::{BlakeTwo256, Dispatchable, StaticLookup, UniqueSaturatedInto}; +use sp_runtime::AccountId32; use sp_std::vec; pub const STAKING_PRECOMPILE_INDEX: u64 = 2049; @@ -75,27 +76,37 @@ impl StakingPrecompile { } fn add_stake(handle: &mut impl PrecompileHandle, data: &[u8]) -> PrecompileResult { + let account_id = + <HashedAddressMapping<BlakeTwo256> as AddressMapping<AccountId32>>::into_account_id( + handle.context().caller, + ); + let (hotkey, _) = get_pubkey(data)?; let amount: U256 = handle.context().apparent_value; let netuid = Self::parse_netuid(data, 0x3E)?; + if !amount.is_zero() { + Self::transfer_back_to_caller(&account_id, amount)?; + } + let amount_sub = <Runtime as pallet_evm::Config>::BalanceConverter::into_substrate_balance(amount) .ok_or(ExitError::OutOfFund)?; - // let (account_id_src, _) = get_pubkey(&CONTRACT_ADDRESS_SS58)?; - // Create the add_stake call let call = RuntimeCall::SubtensorModule(pallet_subtensor::Call::<Runtime>::add_stake { hotkey, netuid, amount_staked: amount_sub.unique_saturated_into(), }); - // let origin = RawOrigin::Signed(account_id_src); - // Dispatch the add_stake call - try_dispatch_runtime_call(handle, call, contract_to_origin(&CONTRACT_ADDRESS_SS58)?) + + try_dispatch_runtime_call(handle, call, RawOrigin::Signed(account_id)) } fn remove_stake(handle: &mut impl PrecompileHandle, data: &[u8]) -> PrecompileResult { + let account_id = + <HashedAddressMapping<BlakeTwo256> as AddressMapping<AccountId32>>::into_account_id( + handle.context().caller, + ); let (hotkey, _) = get_pubkey(data)?; let netuid = Self::parse_netuid(data, 0x5E)?; @@ -115,10 +126,14 @@ impl StakingPrecompile { netuid, amount_unstaked: amount_sub.unique_saturated_into(), }); - try_dispatch_runtime_call(handle, call, contract_to_origin(&CONTRACT_ADDRESS_SS58)?) + try_dispatch_runtime_call(handle, call, RawOrigin::Signed(account_id)) } fn add_proxy(handle: &mut impl PrecompileHandle, data: &[u8]) -> PrecompileResult { + let account_id = + <HashedAddressMapping<BlakeTwo256> as AddressMapping<AccountId32>>::into_account_id( + handle.context().caller, + ); let (delegate, _) = get_pubkey(data)?; let delegate = <Runtime as frame_system::Config>::Lookup::unlookup(delegate); let call = RuntimeCall::Proxy(pallet_proxy::Call::<Runtime>::add_proxy { @@ -127,10 +142,14 @@ impl StakingPrecompile { delay: 0, }); - try_dispatch_runtime_call(handle, call, contract_to_origin(&CONTRACT_ADDRESS_SS58)?) + try_dispatch_runtime_call(handle, call, RawOrigin::Signed(account_id)) } fn remove_proxy(handle: &mut impl PrecompileHandle, data: &[u8]) -> PrecompileResult { + let account_id = + <HashedAddressMapping<BlakeTwo256> as AddressMapping<AccountId32>>::into_account_id( + handle.context().caller, + ); let (delegate, _) = get_pubkey(data)?; let delegate = <Runtime as frame_system::Config>::Lookup::unlookup(delegate); let call = RuntimeCall::Proxy(pallet_proxy::Call::<Runtime>::remove_proxy { @@ -139,7 +158,7 @@ impl StakingPrecompile { delay: 0, }); - try_dispatch_runtime_call(handle, call, contract_to_origin(&CONTRACT_ADDRESS_SS58)?) + try_dispatch_runtime_call(handle, call, RawOrigin::Signed(account_id)) } fn get_stake(data: &[u8]) -> PrecompileResult { @@ -167,17 +186,37 @@ impl StakingPrecompile { }) } - fn parse_netuid(data: &[u8], offset: usize) -> Result<u16, PrecompileFailure> { - if data.len() < offset + 2 { + fn transfer_back_to_caller( + account_id: &AccountId32, + amount: U256, + ) -> Result<(), PrecompileFailure> { + let smart_contract_account_id: AccountId32 = CONTRACT_ADDRESS_SS58.into(); + + let amount_sub = + <Runtime as pallet_evm::Config>::BalanceConverter::into_substrate_balance(amount) + .ok_or(ExitError::OutOfFund)?; + + // Create a transfer call from the smart contract to the caller + let transfer_call = + RuntimeCall::Balances(pallet_balances::Call::<Runtime>::transfer_allow_death { + dest: account_id.clone().into(), + value: amount_sub.unique_saturated_into(), + }); + + // Execute the transfer + let transfer_result = + transfer_call.dispatch(RawOrigin::Signed(smart_contract_account_id).into()); + + if let Err(dispatch_error) = transfer_result { + log::error!( + "Transfer back to caller failed. Error: {:?}", + dispatch_error + ); return Err(PrecompileFailure::Error { - exit_status: ExitError::InvalidRange, + exit_status: ExitError::Other("Transfer back to caller failed".into()), }); } - let mut netuid_bytes = [0u8; 2]; - netuid_bytes.copy_from_slice(get_slice(data, offset, offset + 2)?); - let netuid: u16 = netuid_bytes[1] as u16 | ((netuid_bytes[0] as u16) << 8u16); - - Ok(netuid) + Ok(()) } } diff --git a/runtime/src/precompiles/subnet.rs b/runtime/src/precompiles/subnet.rs index 36f861d48..2701c67d3 100644 --- a/runtime/src/precompiles/subnet.rs +++ b/runtime/src/precompiles/subnet.rs @@ -1,11 +1,13 @@ -use crate::precompiles::{ - contract_to_origin, get_method_id, get_pubkey, get_slice, try_dispatch_runtime_call, -}; +use crate::precompiles::{get_method_id, get_pubkey, get_slice, try_dispatch_runtime_call}; use crate::{Runtime, RuntimeCall}; -use pallet_evm::{ExitError, PrecompileFailure, PrecompileHandle, PrecompileResult}; +use frame_system::RawOrigin; +use pallet_evm::{ + AddressMapping, ExitError, HashedAddressMapping, PrecompileFailure, PrecompileHandle, + PrecompileResult, +}; +use sp_runtime::traits::BlakeTwo256; use sp_runtime::AccountId32; use sp_std::vec; - pub const SUBNET_PRECOMPILE_INDEX: u64 = 2051; // bytes with max lenght 1K pub const MAX_SINGLE_PARAMETER_SIZE: usize = 1024; @@ -51,7 +53,6 @@ impl SubnetPrecompile { let call = match data.len() { 32 => { let (hotkey, _) = get_pubkey(data)?; - RuntimeCall::SubtensorModule( pallet_subtensor::Call::<Runtime>::register_network_with_identity { hotkey, @@ -85,8 +86,13 @@ impl SubnetPrecompile { } }; + let account_id = + <HashedAddressMapping<BlakeTwo256> as AddressMapping<AccountId32>>::into_account_id( + handle.context().caller, + ); + // Dispatch the register_network call - try_dispatch_runtime_call(handle, call, contract_to_origin(&CONTRACT_ADDRESS_SS58)?) + try_dispatch_runtime_call(handle, call, RawOrigin::Signed(account_id)) } fn parse_register_network_parameters( From cdfffaf05a816a9cfd84af2b15e76fe32388ad5d Mon Sep 17 00:00:00 2001 From: open-junius <zhou@opentensor.dev> Date: Fri, 31 Jan 2025 00:22:16 +0800 Subject: [PATCH 142/145] fix clippy --- runtime/src/precompiles/staking.rs | 4 ++-- runtime/src/precompiles/subnet.rs | 1 + 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/runtime/src/precompiles/staking.rs b/runtime/src/precompiles/staking.rs index eefde3a37..8cc1c879a 100644 --- a/runtime/src/precompiles/staking.rs +++ b/runtime/src/precompiles/staking.rs @@ -83,7 +83,7 @@ impl StakingPrecompile { let (hotkey, _) = get_pubkey(data)?; let amount: U256 = handle.context().apparent_value; - let netuid = Self::parse_netuid(data, 0x3E)?; + let netuid = parse_netuid(data, 0x3E)?; if !amount.is_zero() { Self::transfer_back_to_caller(&account_id, amount)?; @@ -108,7 +108,7 @@ impl StakingPrecompile { handle.context().caller, ); let (hotkey, _) = get_pubkey(data)?; - let netuid = Self::parse_netuid(data, 0x5E)?; + let netuid = parse_netuid(data, 0x5E)?; // 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 diff --git a/runtime/src/precompiles/subnet.rs b/runtime/src/precompiles/subnet.rs index 2701c67d3..9944572c5 100644 --- a/runtime/src/precompiles/subnet.rs +++ b/runtime/src/precompiles/subnet.rs @@ -16,6 +16,7 @@ pub const MAX_PARAMETER_SIZE: usize = 3 * MAX_SINGLE_PARAMETER_SIZE; // ss58 public key i.e., the contract sends funds it received to the destination address from the // method parameter. +#[allow(dead_code)] const CONTRACT_ADDRESS_SS58: [u8; 32] = [ 0x3a, 0x86, 0x18, 0xfb, 0xbb, 0x1b, 0xbc, 0x47, 0x86, 0x64, 0xff, 0x53, 0x46, 0x18, 0x0c, 0x35, 0xd0, 0x9f, 0xac, 0x26, 0xf2, 0x02, 0x70, 0x85, 0xb3, 0x1c, 0x56, 0xc1, 0x06, 0x3c, 0x1c, 0xd3, From abec477dab48c06d13fbafaffca1afe6c3ed2c0d Mon Sep 17 00:00:00 2001 From: Greg Zaitsev <gregz@opentensor.dev> Date: Thu, 30 Jan 2025 14:09:02 -0500 Subject: [PATCH 143/145] Validate slippage-safe staking extrinsics --- pallets/subtensor/src/lib.rs | 42 ++++++++- pallets/subtensor/src/tests/staking.rs | 116 +++++++++++++++++++++++++ 2 files changed, 157 insertions(+), 1 deletion(-) diff --git a/pallets/subtensor/src/lib.rs b/pallets/subtensor/src/lib.rs index d346b9241..d694d3d43 100644 --- a/pallets/subtensor/src/lib.rs +++ b/pallets/subtensor/src/lib.rs @@ -1783,7 +1783,7 @@ where } Some(Call::commit_crv3_weights { netuid, .. }) => { if Self::check_weights_min_stake(who, *netuid) { - let priority: u64 = Self::get_priority_set_weights(who, *netuid); + let priority: u64 = Pallet::<T>::get_priority_set_weights(who, *netuid); Ok(ValidTransaction { priority, longevity: 1, @@ -1811,6 +1811,26 @@ where false, )) } + Some(Call::add_stake_limit { + hotkey, + netuid, + amount_staked, + limit_price, + allow_partial, + }) => { + // Calcaulate the maximum amount that can be executed with price limit + let max_amount = Pallet::<T>::get_max_amount_add(*netuid, *limit_price); + + // Fully validate the user input + Self::result_to_validity(Pallet::<T>::validate_add_stake( + who, + hotkey, + *netuid, + *amount_staked, + max_amount, + *allow_partial, + )) + } Some(Call::remove_stake { hotkey, netuid, @@ -1826,6 +1846,26 @@ where false, )) } + Some(Call::remove_stake_limit { + hotkey, + netuid, + amount_unstaked, + limit_price, + allow_partial, + }) => { + // Calcaulate the maximum amount that can be executed with price limit + let max_amount = Pallet::<T>::get_max_amount_remove(*netuid, *limit_price); + + // Fully validate the user input + Self::result_to_validity(Pallet::<T>::validate_remove_stake( + who, + hotkey, + *netuid, + *amount_unstaked, + max_amount, + *allow_partial, + )) + } Some(Call::move_stake { origin_hotkey, destination_hotkey, diff --git a/pallets/subtensor/src/tests/staking.rs b/pallets/subtensor/src/tests/staking.rs index 6a61008a3..3d2172692 100644 --- a/pallets/subtensor/src/tests/staking.rs +++ b/pallets/subtensor/src/tests/staking.rs @@ -2206,6 +2206,122 @@ fn test_stake_below_min_validate() { }); } +// cargo test --package pallet-subtensor --lib -- tests::staking::test_add_stake_limit_validate --exact --show-output +#[test] +fn test_add_stake_limit_validate() { + // Testing the signed extension validate function + // correctly filters the `add_stake` transaction. + + new_test_ext(0).execute_with(|| { + let hotkey = U256::from(533453); + let coldkey = U256::from(55453); + let amount = 300_000_000_000; + + // add network + let netuid: u16 = add_dynamic_network(&hotkey, &coldkey); + + // Force-set alpha in and tao reserve to make price equal 1.5 + let tao_reserve: U96F32 = U96F32::from_num(150_000_000_000_u64); + let alpha_in: U96F32 = U96F32::from_num(100_000_000_000_u64); + SubnetTAO::<Test>::insert(netuid, tao_reserve.to_num::<u64>()); + SubnetAlphaIn::<Test>::insert(netuid, alpha_in.to_num::<u64>()); + let current_price: U96F32 = U96F32::from_num(SubtensorModule::get_alpha_price(netuid)); + assert_eq!(current_price, U96F32::from_num(1.5)); + + // Give it some $$$ in his coldkey balance + SubtensorModule::add_balance_to_coldkey_account(&coldkey, amount); + + // Setup limit price so that it doesn't peak above 4x of current price + // The amount that can be executed at this price is 150 TAO only + // Alpha produced will be equal to 50 = 100 - 150*100/300 + let limit_price = 6_000_000_000; + + // Add stake limit call + let call = RuntimeCall::SubtensorModule(SubtensorCall::add_stake_limit { + hotkey, + netuid, + amount_staked: amount, + limit_price, + allow_partial: false, + }); + + let info: crate::DispatchInfo = + crate::DispatchInfoOf::<<Test as frame_system::Config>::RuntimeCall>::default(); + + let extension = crate::SubtensorSignedExtension::<Test>::new(); + // Submit to the signed extension validate function + let result_no_stake = extension.validate(&coldkey, &call.clone(), &info, 10); + + // Should fail due to slippage + assert_err!( + result_no_stake, + crate::TransactionValidityError::Invalid(crate::InvalidTransaction::Custom( + CustomTransactionError::SlippageTooHigh.into() + )) + ); + }); +} + +// cargo test --package pallet-subtensor --lib -- tests::staking::test_remove_stake_limit_validate --exact --show-output +#[test] +fn test_remove_stake_limit_validate() { + // Testing the signed extension validate function + // correctly filters the `add_stake` transaction. + + new_test_ext(0).execute_with(|| { + let hotkey = U256::from(533453); + let coldkey = U256::from(55453); + let stake_amount = 300_000_000_000; + let unstake_amount = 150_000_000_000; + + // add network + let netuid: u16 = add_dynamic_network(&hotkey, &coldkey); + + // Give the neuron some stake to remove + SubtensorModule::increase_stake_for_hotkey_and_coldkey_on_subnet( + &hotkey, + &coldkey, + netuid, + stake_amount, + ); + + // Forse-set alpha in and tao reserve to make price equal 1.5 + let tao_reserve: U96F32 = U96F32::from_num(150_000_000_000_u64); + let alpha_in: U96F32 = U96F32::from_num(100_000_000_000_u64); + SubnetTAO::<Test>::insert(netuid, tao_reserve.to_num::<u64>()); + SubnetAlphaIn::<Test>::insert(netuid, alpha_in.to_num::<u64>()); + let current_price: U96F32 = U96F32::from_num(SubtensorModule::get_alpha_price(netuid)); + assert_eq!(current_price, U96F32::from_num(1.5)); + + // Setup limit price so that it doesn't drop by more than 10% from current price + let limit_price = 1_350_000_000; + + // Remove stake limit call + let call = RuntimeCall::SubtensorModule(SubtensorCall::remove_stake_limit { + hotkey, + netuid, + amount_unstaked: unstake_amount, + limit_price, + allow_partial: false, + }); + + let info: crate::DispatchInfo = + crate::DispatchInfoOf::<<Test as frame_system::Config>::RuntimeCall>::default(); + + let extension = crate::SubtensorSignedExtension::<Test>::new(); + // Submit to the signed extension validate function + let result_no_stake = extension.validate(&coldkey, &call.clone(), &info, 10); + + // Should fail due to slippage + assert_err!( + result_no_stake, + crate::TransactionValidityError::Invalid(crate::InvalidTransaction::Custom( + CustomTransactionError::SlippageTooHigh.into() + )) + ); + }); +} + #[test] fn test_stake_overflow() { new_test_ext(1).execute_with(|| { From 5bc7f9ca0b99150e98d8937aac0094d1465a5e3c Mon Sep 17 00:00:00 2001 From: Sam Johnson <sam@durosoft.com> Date: Thu, 30 Jan 2025 15:57:25 -0500 Subject: [PATCH 144/145] fix some CI actions not triggering on newly opened pull request --- .github/workflows/check-devnet.yml | 2 +- .github/workflows/check-finney.yml | 2 +- .github/workflows/check-testnet.yml | 2 +- .github/workflows/label-triggers.yml | 3 +++ 4 files changed, 6 insertions(+), 3 deletions(-) diff --git a/.github/workflows/check-devnet.yml b/.github/workflows/check-devnet.yml index c7f39082e..13ebf89cc 100644 --- a/.github/workflows/check-devnet.yml +++ b/.github/workflows/check-devnet.yml @@ -3,7 +3,7 @@ name: Devnet Deploy Check on: pull_request: branches: [devnet, devnet-ready] - types: [labeled, unlabeled, synchronize] + types: [labeled, unlabeled, synchronize, opened] env: CARGO_TERM_COLOR: always diff --git a/.github/workflows/check-finney.yml b/.github/workflows/check-finney.yml index 292bae7ee..98f90fc8e 100644 --- a/.github/workflows/check-finney.yml +++ b/.github/workflows/check-finney.yml @@ -3,7 +3,7 @@ name: Finney Deploy Check on: pull_request: branches: [finney, main] - types: [labeled, unlabeled, synchronize] + types: [labeled, unlabeled, synchronize, opened] env: CARGO_TERM_COLOR: always diff --git a/.github/workflows/check-testnet.yml b/.github/workflows/check-testnet.yml index 03c7e8f8a..7c8532ec5 100644 --- a/.github/workflows/check-testnet.yml +++ b/.github/workflows/check-testnet.yml @@ -3,7 +3,7 @@ name: Testnet Deploy Check on: pull_request: branches: [testnet, testnet-ready] - types: [labeled, unlabeled, synchronize] + types: [labeled, unlabeled, synchronize, opened] env: CARGO_TERM_COLOR: always diff --git a/.github/workflows/label-triggers.yml b/.github/workflows/label-triggers.yml index d32396e07..f3c330f85 100644 --- a/.github/workflows/label-triggers.yml +++ b/.github/workflows/label-triggers.yml @@ -3,6 +3,9 @@ on: pull_request: types: - labeled + - unlabeled + - synchronize + - opened permissions: issues: write From 645689dd4413e6dc9382960c87a19d9c9de67ce0 Mon Sep 17 00:00:00 2001 From: Greg Zaitsev <gregz@opentensor.dev> Date: Thu, 30 Jan 2025 17:09:40 -0500 Subject: [PATCH 145/145] Double-size SubnetVolume to u128 --- pallets/subtensor/src/lib.rs | 7 ++++++- pallets/subtensor/src/rpc_info/dynamic_info.rs | 4 ++-- pallets/subtensor/src/rpc_info/metagraph.rs | 4 ++-- pallets/subtensor/src/staking/stake_utils.rs | 8 ++++---- 4 files changed, 14 insertions(+), 9 deletions(-) diff --git a/pallets/subtensor/src/lib.rs b/pallets/subtensor/src/lib.rs index 56646f857..f789d8191 100644 --- a/pallets/subtensor/src/lib.rs +++ b/pallets/subtensor/src/lib.rs @@ -234,6 +234,11 @@ pub mod pallet { } #[pallet::type_value] /// Default value for zero. + pub fn DefaultZeroU128<T: Config>() -> u128 { + 0 + } + #[pallet::type_value] + /// Default value for zero. pub fn DefaultZeroU16<T: Config>() -> u16 { 0 } @@ -907,7 +912,7 @@ pub mod pallet { pub type DynamicBlock<T> = StorageValue<_, u64, ValueQuery>; #[pallet::storage] // --- MAP ( netuid ) --> total_volume | The total amount of TAO bought and sold since the start of the network. pub type SubnetVolume<T: Config> = - StorageMap<_, Identity, u16, u64, ValueQuery, DefaultZeroU64<T>>; + StorageMap<_, Identity, u16, u128, ValueQuery, DefaultZeroU128<T>>; #[pallet::storage] // --- MAP ( netuid ) --> tao_in_subnet | Returns the amount of TAO in the subnet. pub type SubnetTAO<T: Config> = StorageMap<_, Identity, u16, u64, ValueQuery, DefaultZeroU64<T>>; diff --git a/pallets/subtensor/src/rpc_info/dynamic_info.rs b/pallets/subtensor/src/rpc_info/dynamic_info.rs index bbee27c55..27d10dc2d 100644 --- a/pallets/subtensor/src/rpc_info/dynamic_info.rs +++ b/pallets/subtensor/src/rpc_info/dynamic_info.rs @@ -4,7 +4,7 @@ use codec::Compact; use frame_support::pallet_prelude::{Decode, Encode}; use subtensor_macros::freeze_struct; -#[freeze_struct("1be5a1e26a82082f")] +#[freeze_struct("a5cdc80d655398e9")] #[derive(Decode, Encode, PartialEq, Eq, Clone, Debug)] pub struct DynamicInfo<T: Config> { netuid: Compact<u16>, @@ -24,7 +24,7 @@ pub struct DynamicInfo<T: Config> { tao_in_emission: Compact<u64>, pending_alpha_emission: Compact<u64>, pending_root_emission: Compact<u64>, - subnet_volume: Compact<u64>, + subnet_volume: Compact<u128>, network_registered_at: Compact<u64>, subnet_identity: Option<SubnetIdentity>, } diff --git a/pallets/subtensor/src/rpc_info/metagraph.rs b/pallets/subtensor/src/rpc_info/metagraph.rs index ee3864932..267af1d05 100644 --- a/pallets/subtensor/src/rpc_info/metagraph.rs +++ b/pallets/subtensor/src/rpc_info/metagraph.rs @@ -6,7 +6,7 @@ use frame_support::pallet_prelude::{Decode, Encode}; use substrate_fixed::types::I64F64; use subtensor_macros::freeze_struct; -#[freeze_struct("fa24d156067e5eb9")] +#[freeze_struct("7c5fe907490c5d5e")] #[derive(Decode, Encode, PartialEq, Eq, Clone, Debug)] pub struct Metagraph<T: Config> { // Subnet index @@ -38,7 +38,7 @@ pub struct Metagraph<T: Config> { tao_in_emission: Compact<u64>, // amount of tao injected per block pending_alpha_emission: Compact<u64>, // pending alpha to be distributed pending_root_emission: Compact<u64>, // panding tao for root divs to be distributed - subnet_volume: Compact<u64>, // volume of the subnet in TAO + subnet_volume: Compact<u128>, // volume of the subnet in TAO // Hparams for epoch rho: Compact<u16>, // subnet rho param diff --git a/pallets/subtensor/src/staking/stake_utils.rs b/pallets/subtensor/src/staking/stake_utils.rs index 4c446947f..147490dd8 100644 --- a/pallets/subtensor/src/staking/stake_utils.rs +++ b/pallets/subtensor/src/staking/stake_utils.rs @@ -557,9 +557,9 @@ impl<T: Config> Pallet<T> { TotalStake::<T>::mutate(|total| { *total = total.saturating_add(tao); }); - // Step 8. Decrease Alpha reserves. + // Step 8. Increase total subnet TAO volume. SubnetVolume::<T>::mutate(netuid, |total| { - *total = total.saturating_add(tao); + *total = total.saturating_add(tao.into()); }); // Step 9. Return the alpha received. alpha @@ -589,9 +589,9 @@ impl<T: Config> Pallet<T> { TotalStake::<T>::mutate(|total| { *total = total.saturating_sub(tao); }); - // Step 8. Decrease Alpha reserves. + // Step 8. Increase total subnet TAO volume. SubnetVolume::<T>::mutate(netuid, |total| { - *total = total.saturating_add(tao); + *total = total.saturating_add(tao.into()); }); // Step 9. Return the tao received. tao