From c66aeb911f4726bfb6c40eca0c2bfe4980f0853e Mon Sep 17 00:00:00 2001 From: const Date: Fri, 5 Jul 2024 16:57:48 -0500 Subject: [PATCH 01/51] initial --- pallets/subtensor/src/lib.rs | 12 +++ pallets/subtensor/src/staking.rs | 156 +++++++++++++++++++++++-------- 2 files changed, 127 insertions(+), 41 deletions(-) diff --git a/pallets/subtensor/src/lib.rs b/pallets/subtensor/src/lib.rs index 1267c5cc6..0550a823d 100644 --- a/pallets/subtensor/src/lib.rs +++ b/pallets/subtensor/src/lib.rs @@ -380,6 +380,18 @@ pub mod pallet { ValueQuery, DefaultAccountTake, >; + + + + #[pallet::storage] // --- MAP ( cold ) --> Vec | Returns a list of keys to drain to, if there are two, we extend the period. + pub type Drain = StorageMap<_, Blake2_128Concat, T::AccountId, Vec, ValueQuery>; + + + #[pallet::storage] // --- MAP ( cold ) --> block_when_drain_occurs | Returns the block when the + pub type Period = StorageMap<_, Blake2_128Concat, T::AccountId, Vec, ValueQuery>; + + + /// -- ITEM (switches liquid alpha on) #[pallet::type_value] pub fn DefaultLiquidAlpha() -> bool { diff --git a/pallets/subtensor/src/staking.rs b/pallets/subtensor/src/staking.rs index 09f6d68ac..552107e91 100644 --- a/pallets/subtensor/src/staking.rs +++ b/pallets/subtensor/src/staking.rs @@ -869,55 +869,129 @@ impl Pallet { // Ensure the new coldkey is different from the current one ensure!(current_coldkey != new_coldkey, Error::::SameColdkey); - // Get all the hotkeys associated with this coldkey - let hotkeys: Vec = OwnedHotkeys::::get(¤t_coldkey); + // Get the current wallets to drain to. + let mut drain_wallets: Vec = Drain::::get( ¤t_coldkey ); - // iterate over all hotkeys. - for next_hotkey in hotkeys { - ensure!( - Self::hotkey_account_exists(&next_hotkey), - Error::::HotKeyAccountNotExists - ); - ensure!( - Self::coldkey_owns_hotkey(¤t_coldkey, &next_hotkey), - Error::::NonAssociatedColdKey - ); + // DOS protection: If there are more than 10 wallets to drain to, we extend the period. + // if drain_wallets.len() > 10 { + // // Set the new coldkey as the first drain wallet. + // drain_wallets.push(new_coldkey); + // Drain::::insert(current_coldkey, drain_wallets); + // } + + // Check if the new coldkey is already in the drain wallets list + ensure!( + !drain_wallets.contains(&new_coldkey), + Error::::DuplicateColdkey + ); + + // Extend the period if we have two unique keys in the drain + if drain_wallets.len() > 0 { + + // Get the current block to drain + let mut drain_block: u64 = DrainBlock::::get( current_coldkey ); + + // Extend the block to drain. + let extended_block: u64 = drain_block + 7200 * 7; + + // Set the new drain block. + DrainBlock::::insert( current_coldkey, extended_block ); + + // Extend the period. + let mut coldkeys_to_drain: Vec = ColdkeysToDrainOnBlock::::get( extended_block ); + + // Add the coldkey to drain on this block. + coldkeys_to_drain.push( current_coldkey ); + + // Set the new coldkeys to drain here. + ColdkeysToDrainOnBlock::::insert( extended_block, coldkeys_to_drain.clone() ); + + // Clear the pending keys. + Drain::::remove(¤t_coldkey); + + } else { + // There are not other wallets pending. + + // Extend the wallet to drain to. + drain_wallets.push(new_coldkey); + + // Push the change. + Drain::::insert( current_coldkey, drain_wallets ); + } + + Ok(()) + } - // Get the current stake - let current_stake: u64 = - Self::get_stake_for_coldkey_and_hotkey(¤t_coldkey, &next_hotkey); - - // Unstake all balance if there's any stake - if current_stake > 0 { - Self::do_remove_stake( - RawOrigin::Signed(current_coldkey.clone()).into(), - next_hotkey.clone(), - current_stake, - )?; + pub fn drain_all_pending_coldkeys(block_num){ + + // Get the block number + let current_block: u64 = Self::get_current_block_as_u64(); + + // Get the coldkeys to drain here. + let mut coldkeys_to_drain: Vec = ColdkeysToDrainOnBlock::::get( current_block ); + + // Iterate over all keys in Drain and call drain_to_pending_coldkeys for each + for coldkey_i in coldkeys_to_drain.iter() { + + // Get the wallets to drain to for this coldkey. + let wallets_to_drain: Vec = Drain::::get( coldkey_i ); + + // If there are no wallets to drain to, remove the key from the drain map. + if wallets_to_drain.len() == 0 { + + // Remove the key from the drain map + Drain::::remove( &coldkey ); + continue + } + // If there is only 1 wallet to drain perform the drain operation. + if wallets_to_drain.len() == 1 { + + // Get the wallet to drain to. + let wallet_to_drain_to: T::AccountId = wallets_to_drain[0]; + + // Perform the drain. + Self::drain_from_coldkeyA_to_coldkey_B( &coldkey_i, &wallet_to_drain_to ); + + // Remove the key from the drain map + Drain::::remove( &coldkey ); + + // Set the new drain block. + DrainBlock::::remove( &coldkey ); } } + } - let total_balance = Self::get_coldkey_balance(¤t_coldkey); - log::info!("Total Bank Balance: {:?}", total_balance); - // Ensure there's a balance to transfer - ensure!(!total_balance.is_zero(), Error::::NoBalanceToTransfer); + pub fn drain_from_coldkeyA_to_coldkey_B( coldkeyA: &T::AccountId, coldkeyB: &T::AccountId ) { - // Attempt to transfer the entire total balance to the new coldkey - T::Currency::transfer( - ¤t_coldkey, - &new_coldkey, - total_balance, - Preservation::Expendable, - )?; + // Get the hotkeys associated with coldkeyA. + let coldkeyA_hotkeys: Vec = StakingHotkeys::::get( &coldkeyA ); - // Emit the event - Self::deposit_event(Event::AllBalanceUnstakedAndTransferredToNewColdkey { - current_coldkey: current_coldkey.clone(), - new_coldkey: new_coldkey.clone(), - total_balance, - }); + // Iterate over all the hotkeys associated with this coldkey + for hotkey_i in coldkeyA_hotkeys.iter() { + + // Get the current stake from coldkeyA to hotkey_i. + let all_current_stake_i: u64 = Self::get_stake_for_coldkey_and_hotkey( &coldkeyA, &hotkey_i ); + + // We remove the balance from the hotkey acount equal to all of it. + Self::decrease_stake_on_coldkey_hotkey_account( &coldkeyA, &hotkey_i, all_current_stake_i ); + + // We add the balance to the coldkey. If the above fails we will not credit this coldkey. + Self::add_balance_to_coldkey_account( &coldkeyA, all_current_stake_i ); + } + + // Get the total balance here. + let total_balance = Self::get_coldkey_balance( &coldkeyA ); + + if !total_balance.is_zero() { + // Attempt to transfer the entire total balance to coldkeyB. + T::Currency::transfer( + ¤t_coldkey, + &new_coldkey, + total_balance, + Preservation::Expendable, + ); + } - Ok(()) } } From 4a398957de1029c51ef50302df67ace9f778d86b Mon Sep 17 00:00:00 2001 From: const Date: Fri, 5 Jul 2024 16:58:39 -0500 Subject: [PATCH 02/51] merge --- pallets/subtensor/src/staking.rs | 7 ------- 1 file changed, 7 deletions(-) diff --git a/pallets/subtensor/src/staking.rs b/pallets/subtensor/src/staking.rs index 552107e91..080a34d5b 100644 --- a/pallets/subtensor/src/staking.rs +++ b/pallets/subtensor/src/staking.rs @@ -872,13 +872,6 @@ impl Pallet { // Get the current wallets to drain to. let mut drain_wallets: Vec = Drain::::get( ¤t_coldkey ); - // DOS protection: If there are more than 10 wallets to drain to, we extend the period. - // if drain_wallets.len() > 10 { - // // Set the new coldkey as the first drain wallet. - // drain_wallets.push(new_coldkey); - // Drain::::insert(current_coldkey, drain_wallets); - // } - // Check if the new coldkey is already in the drain wallets list ensure!( !drain_wallets.contains(&new_coldkey), From bfea68a65a25012afc3a3cdbcffc7e56d5ff98b6 Mon Sep 17 00:00:00 2001 From: const Date: Fri, 5 Jul 2024 17:50:38 -0500 Subject: [PATCH 03/51] push changes --- pallets/subtensor/src/errors.rs | 2 + pallets/subtensor/src/lib.rs | 26 +++-- pallets/subtensor/src/migration.rs | 65 ++++++++++++ pallets/subtensor/src/registration.rs | 1 + pallets/subtensor/src/root.rs | 7 +- pallets/subtensor/src/staking.rs | 141 +++++++++++++++++--------- pallets/subtensor/src/swap.rs | 21 +++- pallets/subtensor/tests/swap.rs | 4 +- runtime/src/lib.rs | 2 +- 9 files changed, 208 insertions(+), 61 deletions(-) diff --git a/pallets/subtensor/src/errors.rs b/pallets/subtensor/src/errors.rs index 3e30c094c..bd113be3d 100644 --- a/pallets/subtensor/src/errors.rs +++ b/pallets/subtensor/src/errors.rs @@ -146,5 +146,7 @@ mod errors { NoBalanceToTransfer, /// Same coldkey SameColdkey, + /// The coldkey is in arbitration + ColdkeyIsInArbitration, } } diff --git a/pallets/subtensor/src/lib.rs b/pallets/subtensor/src/lib.rs index 0550a823d..6e705b568 100644 --- a/pallets/subtensor/src/lib.rs +++ b/pallets/subtensor/src/lib.rs @@ -381,16 +381,17 @@ pub mod pallet { DefaultAccountTake, >; - - + /// Default value for hotkeys. + #[pallet::type_value] + pub fn EmptyAccounts() -> Vec { vec![] } #[pallet::storage] // --- MAP ( cold ) --> Vec | Returns a list of keys to drain to, if there are two, we extend the period. - pub type Drain = StorageMap<_, Blake2_128Concat, T::AccountId, Vec, ValueQuery>; - - - #[pallet::storage] // --- MAP ( cold ) --> block_when_drain_occurs | Returns the block when the - pub type Period = StorageMap<_, Blake2_128Concat, T::AccountId, Vec, ValueQuery>; + pub type ColdkeysToDrainTo = StorageMap<_, Blake2_128Concat, T::AccountId, Vec, ValueQuery, EmptyAccounts>; + #[pallet::storage] // --- MAP ( u64 ) --> Vec | Coldkeys to drain on the specific block. + pub type ColdkeysToDrainOnBlock = StorageMap<_, Identity, u64, Vec, ValueQuery, EmptyAccounts>; + #[pallet::storage] // --- DMAP ( cold ) --> Vec | Maps coldkey to hotkeys that stake to it + pub type StakingHotkeys = StorageMap<_, Blake2_128Concat, T::AccountId, Vec, ValueQuery>; /// -- ITEM (switches liquid alpha on) #[pallet::type_value] @@ -1237,6 +1238,13 @@ pub mod pallet { Stake::::insert(hotkey.clone(), coldkey.clone(), stake); + // Update StakingHotkeys map + let mut staking_hotkeys = StakingHotkeys::::get(coldkey); + if !staking_hotkeys.contains(hotkey) { + staking_hotkeys.push(hotkey.clone()); + StakingHotkeys::::insert(coldkey, staking_hotkeys); + } + next_uid = next_uid.checked_add(1).expect( "should not have total number of hotkey accounts larger than u16::MAX", ); @@ -1349,7 +1357,9 @@ pub mod pallet { // Doesn't check storage version. TODO: Remove after upgrade .saturating_add(migration::migration5_total_issuance::(false)) // Populate OwnedHotkeys map for coldkey swap. Doesn't update storage vesion. - .saturating_add(migration::migrate_populate_owned::()); + .saturating_add(migration::migrate_populate_owned::()) + // Populate StakingHotkeys map for coldkey swap. Doesn't update storage vesion. + .saturating_add(migration::migrate_populate_staking_hotkeys::()); weight } diff --git a/pallets/subtensor/src/migration.rs b/pallets/subtensor/src/migration.rs index 5dbdd5f42..400030450 100644 --- a/pallets/subtensor/src/migration.rs +++ b/pallets/subtensor/src/migration.rs @@ -539,3 +539,68 @@ pub fn migrate_populate_owned() -> Weight { Weight::zero() } } + +/// Populate the StakingHotkeys map from Stake map +pub fn migrate_populate_staking_hotkeys() -> Weight { + // Setup migration weight + let mut weight = T::DbWeight::get().reads(1); + let migration_name = "Populate StakingHotkeys map"; + + // Check if this migration is needed (if StakingHotkeys map is empty) + let migrate = StakingHotkeys::::iter().next().is_none(); + + // Only runs if the migration is needed + if migrate { + info!(target: LOG_TARGET_1, ">>> Starting Migration: {}", migration_name); + + let mut longest_hotkey_vector: usize = 0; + let mut longest_coldkey: Option = None; + let mut keys_touched: u64 = 0; + let mut storage_reads: u64 = 0; + let mut storage_writes: u64 = 0; + + // Iterate through all Owner entries + Stake::::iter().for_each(|(hotkey, coldkey, stake)| { + storage_reads = storage_reads.saturating_add(1); // Read from Owner storage + if stake > 0 { + let mut hotkeys = StakingHotkeys::::get(&coldkey); + storage_reads = storage_reads.saturating_add(1); // Read from StakingHotkeys storage + + // Add the hotkey if it's not already in the vector + if !hotkeys.contains(&hotkey) { + hotkeys.push(hotkey); + keys_touched = keys_touched.saturating_add(1); + + // Update longest hotkey vector info + if longest_hotkey_vector < hotkeys.len() { + longest_hotkey_vector = hotkeys.len(); + longest_coldkey = Some(coldkey.clone()); + } + + // Update the StakingHotkeys storage + StakingHotkeys::::insert(&coldkey, hotkeys); + storage_writes = storage_writes.saturating_add(1); // Write to StakingHotkeys storage + } + + // Accrue weight for reads and writes + weight = weight.saturating_add(T::DbWeight::get().reads_writes(2, 1)); + } + }); + + // Log migration results + info!( + target: LOG_TARGET_1, + "Migration {} finished. Keys touched: {}, Longest hotkey vector: {}, Storage reads: {}, Storage writes: {}", + migration_name, keys_touched, longest_hotkey_vector, storage_reads, storage_writes + ); + if let Some(c) = longest_coldkey { + info!(target: LOG_TARGET_1, "Longest hotkey vector is controlled by: {:?}", c); + } + + weight + } else { + info!(target: LOG_TARGET_1, "Migration {} already done!", migration_name); + Weight::zero() + } +} + diff --git a/pallets/subtensor/src/registration.rs b/pallets/subtensor/src/registration.rs index 4688bcbb5..bd162a5cf 100644 --- a/pallets/subtensor/src/registration.rs +++ b/pallets/subtensor/src/registration.rs @@ -41,6 +41,7 @@ impl Pallet { ) -> DispatchResult { // --- 1. Check that the caller has signed the transaction. (the coldkey of the pairing) let coldkey = ensure_signed(origin)?; + ensure!(!Self::coldkey_is_locked(&coldkey), Error::::ColdkeyIsInArbitration); log::info!( "do_registration( coldkey:{:?} netuid:{:?} hotkey:{:?} )", coldkey, diff --git a/pallets/subtensor/src/root.rs b/pallets/subtensor/src/root.rs index 9e0327fb3..e65524909 100644 --- a/pallets/subtensor/src/root.rs +++ b/pallets/subtensor/src/root.rs @@ -483,6 +483,7 @@ impl Pallet { // --- 1. Ensure that the call originates from a signed source and retrieve the caller's account ID (coldkey). let coldkey = ensure_signed(origin)?; + ensure!(!Self::coldkey_is_locked(&coldkey), Error::::ColdkeyIsInArbitration); log::info!( "do_root_register( coldkey: {:?}, hotkey: {:?} )", coldkey, @@ -623,6 +624,7 @@ impl Pallet { ) -> dispatch::DispatchResult { // Check the caller's signature. This is the coldkey of a registered account. let coldkey = ensure_signed(origin)?; + ensure!(!Self::coldkey_is_locked(&coldkey), Error::::ColdkeyIsInArbitration); log::info!( "do_set_root_weights( origin:{:?} netuid:{:?}, uids:{:?}, values:{:?})", coldkey, @@ -744,6 +746,7 @@ impl Pallet { ) -> DispatchResultWithPostInfo { // --- 1. Ensure that the caller has signed with their coldkey. let coldkey = ensure_signed(origin.clone())?; + ensure!(!Self::coldkey_is_locked(&coldkey), Error::::ColdkeyIsInArbitration); // --- 2. Ensure that the calling coldkey owns the associated hotkey. ensure!( @@ -797,7 +800,8 @@ impl Pallet { pub fn user_add_network(origin: T::RuntimeOrigin) -> dispatch::DispatchResult { // --- 0. Ensure the caller is a signed user. let coldkey = ensure_signed(origin)?; - + ensure!(!Self::coldkey_is_locked(&coldkey), Error::::ColdkeyIsInArbitration); + // --- 1. Rate limit for network registrations. let current_block = Self::get_current_block_as_u64(); let last_lock_block = Self::get_network_last_lock_block(); @@ -885,6 +889,7 @@ impl Pallet { pub fn user_remove_network(origin: T::RuntimeOrigin, netuid: u16) -> dispatch::DispatchResult { // --- 1. Ensure the function caller is a signed user. let coldkey = ensure_signed(origin)?; + ensure!(!Self::coldkey_is_locked(&coldkey), Error::::ColdkeyIsInArbitration); // --- 2. Ensure this subnet exists. ensure!( diff --git a/pallets/subtensor/src/staking.rs b/pallets/subtensor/src/staking.rs index 080a34d5b..3deef551b 100644 --- a/pallets/subtensor/src/staking.rs +++ b/pallets/subtensor/src/staking.rs @@ -46,6 +46,7 @@ impl Pallet { ) -> dispatch::DispatchResult { // --- 1. We check the coldkey signuture. let coldkey = ensure_signed(origin)?; + ensure!(!Self::coldkey_is_locked(&coldkey), Error::::ColdkeyIsInArbitration); log::info!( "do_become_delegate( origin:{:?} hotkey:{:?}, take:{:?} )", coldkey, @@ -135,6 +136,7 @@ impl Pallet { hotkey, take ); + ensure!(!Self::coldkey_is_locked(&coldkey), Error::::ColdkeyIsInArbitration); // --- 2. Ensure we are delegating a known key. // Ensure that the coldkey is the owner. @@ -207,6 +209,7 @@ impl Pallet { hotkey, take ); + ensure!(!Self::coldkey_is_locked(&coldkey), Error::::ColdkeyIsInArbitration); // --- 2. Ensure we are delegating a known key. // Ensure that the coldkey is the owner. @@ -292,6 +295,7 @@ impl Pallet { hotkey, stake_to_be_added ); + ensure!(!Self::coldkey_is_locked(&coldkey), Error::::ColdkeyIsInArbitration); // Ensure the callers coldkey has enough stake to perform the transaction. ensure!( @@ -404,6 +408,7 @@ impl Pallet { hotkey, stake_to_be_removed ); + ensure!(!Self::coldkey_is_locked(&coldkey), Error::::ColdkeyIsInArbitration); // Ensure that the hotkey account exists this is only possible through registration. ensure!( @@ -569,6 +574,13 @@ impl Pallet { hotkeys.push(hotkey.clone()); OwnedHotkeys::::insert(coldkey, hotkeys); } + + // Update StakingHotkeys map + let mut staking_hotkeys = StakingHotkeys::::get(coldkey); + if !staking_hotkeys.contains(hotkey) { + staking_hotkeys.push(hotkey.clone()); + StakingHotkeys::::insert(coldkey, staking_hotkeys); + } } } @@ -648,6 +660,13 @@ impl Pallet { Stake::::get(hotkey, coldkey).saturating_add(increment), ); TotalStake::::put(TotalStake::::get().saturating_add(increment)); + + // Update StakingHotkeys map + let mut staking_hotkeys = StakingHotkeys::::get(coldkey); + if !staking_hotkeys.contains(hotkey) { + staking_hotkeys.push(hotkey.clone()); + StakingHotkeys::::insert(coldkey, staking_hotkeys); + } } // Decreases the stake on the cold - hot pairing by the decrement while decreasing other counters. @@ -668,6 +687,8 @@ impl Pallet { Stake::::get(hotkey, coldkey).saturating_sub(decrement), ); TotalStake::::put(TotalStake::::get().saturating_sub(decrement)); + + // TODO: Tech debt: Remove StakingHotkeys entry if stake goes to 0 } /// Empties the stake associated with a given coldkey-hotkey account pairing. @@ -693,6 +714,11 @@ impl Pallet { TotalStake::::mutate(|stake| *stake = stake.saturating_sub(current_stake)); TotalIssuance::::mutate(|issuance| *issuance = issuance.saturating_sub(current_stake)); + // Update StakingHotkeys map + let mut staking_hotkeys = StakingHotkeys::::get(coldkey); + staking_hotkeys.retain(|h| h != hotkey); + StakingHotkeys::::insert(coldkey, staking_hotkeys); + current_stake } @@ -866,90 +892,108 @@ impl Pallet { current_coldkey: T::AccountId, new_coldkey: T::AccountId, ) -> DispatchResult { - // Ensure the new coldkey is different from the current one - ensure!(current_coldkey != new_coldkey, Error::::SameColdkey); // Get the current wallets to drain to. - let mut drain_wallets: Vec = Drain::::get( ¤t_coldkey ); + let mut coldkeys_to_drain_to: Vec = ColdkeyToDrainTo::::get( ¤t_coldkey ); // Check if the new coldkey is already in the drain wallets list ensure!( - !drain_wallets.contains(&new_coldkey), + !coldkeys_to_drain_to.contains( &new_coldkey ), Error::::DuplicateColdkey ); - // Extend the period if we have two unique keys in the drain - if drain_wallets.len() > 0 { - - // Get the current block to drain - let mut drain_block: u64 = DrainBlock::::get( current_coldkey ); + // Add the wallet to the drain wallets. + if coldkeys_to_drain_to.len() == 0 || coldkeys_to_drain_to.len() == 1 { - // Extend the block to drain. - let extended_block: u64 = drain_block + 7200 * 7; - - // Set the new drain block. - DrainBlock::::insert( current_coldkey, extended_block ); + // Extend the wallet to drain to. + coldkeys_to_drain_to.push(new_coldkey); - // Extend the period. - let mut coldkeys_to_drain: Vec = ColdkeysToDrainOnBlock::::get( extended_block ); + // Push the change. + ColdkeysToDrainTo::::insert( current_coldkey, drain_wallets ); + } - // Add the coldkey to drain on this block. - coldkeys_to_drain.push( current_coldkey ); + // If this is the first time we have seen this key we will put the drain period to be in 1 week. + if coldkeys_to_drain_to.len() == 0 { - // Set the new coldkeys to drain here. - ColdkeysToDrainOnBlock::::insert( extended_block, coldkeys_to_drain.clone() ); + // Get the current block. + let current_block: u64 = Self::get_current_block_as_u64(); - // Clear the pending keys. - Drain::::remove(¤t_coldkey); + // Next arbitrage period + let next_arbitrage_period: u64 = current_block + 7200 * 7; - } else { - // There are not other wallets pending. + // First time seeing this key lets push the drain moment to 1 week in the future. + let mut next_period_coldkeys_to_drain: Vec = ColdkeysToDrainOnBlock::::get( next_arbitrage_period ); - // Extend the wallet to drain to. - drain_wallets.push(new_coldkey); + // Add this new coldkey to these coldkeys + // Sanity Check. + if !next_period_coldkeys_to_drain.contains(&coldkey_i) { + next_period_coldkeys_to_drain.push(coldkey_i); + } - // Push the change. - Drain::::insert( current_coldkey, drain_wallets ); + // Set the new coldkeys to drain here. + ColdkeysToDrainOnBlock::::insert( next_arbitrage_period, next_period_coldkeys_to_drain ); } + // Return true. Ok(()) } - pub fn drain_all_pending_coldkeys(block_num){ + + // Chain opens, this is the only allowed transaction. + // 1. Someone calls drain with key C1, DestA, DestA is added to ColdToBeDrained and C1 is given a drain block in 7 days. + // 2. Someone else calls drain with key C1, DestB, DestB is added to ColdToBeDrained and C1 already has a drain block, not updated. + // 3. Someone calls drain key with C1, DestC. Dest C is not added to the ColdToBeDrained. No-op/ + // 4. 7200 * 7 blocks progress. + // 5. ColdkeysToDrainOnBlock( block ) returns [ C1 ] + // 6. ColdToBeDrained( C1 ) returns [ DestA, DestB ] and is Drained + // 7. len([ DestA, DestB ]) == 2, set the drain block to be 7 days in the future. + // 8. Someone calls drain with key C1, DestA, DestD is added to ColdToBeDrained and C1 is given a drain block in 7 days. + // 9. 7200 * 7 blocks progress. + // 10. ColdkeysToDrainOnBlock( block ) returns [ C1 ] + // 11. ColdToBeDrained( C1 ) returns [ DestD ] and is Drained + // 12. len([ DestD ]) == 1, call_drain( C1, DestD ) + + pub fn drain_all_pending_coldkeys() { // Get the block number let current_block: u64 = Self::get_current_block_as_u64(); // Get the coldkeys to drain here. - let mut coldkeys_to_drain: Vec = ColdkeysToDrainOnBlock::::get( current_block ); + let mut source_coldkeys: Vec = ColdkeysToDrainOnBlock::::get( current_block ); + ColdkeysToDrainOnBlock::::remove( current_block ); // Iterate over all keys in Drain and call drain_to_pending_coldkeys for each - for coldkey_i in coldkeys_to_drain.iter() { + for coldkey_i in source_coldkeys.iter() { // Get the wallets to drain to for this coldkey. - let wallets_to_drain: Vec = Drain::::get( coldkey_i ); + if !ColdkeysToDrainTo::::contains_key( &coldkey_i ) { continue } // Sanity Check. + let destinations_coldkeys: Vec = ColdkeysToDrainTo::::get( coldkey_i ); + ColdkeysToDrainTo::::remove( &coldkey_i ); - // If there are no wallets to drain to, remove the key from the drain map. - if wallets_to_drain.len() == 0 { + // If the wallets to drain is > 1, we extend the period. + if destinations_coldkeys.len() > 1 { - // Remove the key from the drain map - Drain::::remove( &coldkey ); - continue - } - // If there is only 1 wallet to drain perform the drain operation. - if wallets_to_drain.len() == 1 { + // Next arbitrage period + let next_arbitrage_period: u64 = current_block + 7200 * 30; - // Get the wallet to drain to. + // Get the coldkeys to drain at the next arbitrage period. + let mut next_period_coldkeys_to_drain: Vec = ColdkeysToDrainOnBlock::::get( next_arbitrage_period ); + + // Add this new coldkey to these coldkeys + // Sanity Check. + if !next_period_coldkeys_to_drain.contains(&coldkey_i) { + next_period_coldkeys_to_drain.push(coldkey_i); + } + + // Set the new coldkeys to drain here. + ColdkeysToDrainOnBlock::::insert( next_arbitrage_period, next_period_coldkeys_to_drain ); + + } else if destinations_coldkeys.len() == 1 { + // ONLY 1 wallet: Get the wallet to drain to. let wallet_to_drain_to: T::AccountId = wallets_to_drain[0]; // Perform the drain. Self::drain_from_coldkeyA_to_coldkey_B( &coldkey_i, &wallet_to_drain_to ); - - // Remove the key from the drain map - Drain::::remove( &coldkey ); - - // Set the new drain block. - DrainBlock::::remove( &coldkey ); } } } @@ -985,6 +1029,9 @@ impl Pallet { Preservation::Expendable, ); } + } + pub fn coldkey_is_locked( coldkey: &T::AccountId ) -> bool { + ColdkeyToDrainTo::::contains_key( &coldkey ) } } diff --git a/pallets/subtensor/src/swap.rs b/pallets/subtensor/src/swap.rs index 49af72a3f..137956dc3 100644 --- a/pallets/subtensor/src/swap.rs +++ b/pallets/subtensor/src/swap.rs @@ -28,6 +28,7 @@ impl Pallet { new_hotkey: &T::AccountId, ) -> DispatchResultWithPostInfo { let coldkey = ensure_signed(origin)?; + ensure!(!Self::coldkey_is_locked(&coldkey), Error::::ColdkeyIsInArbitration); let mut weight = T::DbWeight::get().reads(2); @@ -115,7 +116,8 @@ impl Pallet { old_coldkey: &T::AccountId, new_coldkey: &T::AccountId, ) -> DispatchResultWithPostInfo { - ensure_signed(origin)?; + let coldkey_performing_swap = ensure_signed(origin)?; + ensure!(!Self::coldkey_is_locked(&coldkey_performing_swap), Error::::ColdkeyIsInArbitration); let mut weight = T::DbWeight::get().reads(2); @@ -272,12 +274,22 @@ impl Pallet { for (coldkey, stake_amount) in stakes { Stake::::insert(new_hotkey, &coldkey, stake_amount); writes = writes.saturating_add(1u64); // One write for insert + + // Update StakingHotkeys map + let mut staking_hotkeys = StakingHotkeys::::get(&coldkey); + if !staking_hotkeys.contains(new_hotkey) { + staking_hotkeys.push(new_hotkey.clone()); + StakingHotkeys::::insert(coldkey.clone(), staking_hotkeys); + writes = writes.saturating_add(1u64); // One write for insert + } } // Clear the prefix for the old hotkey after transferring all stakes let _ = Stake::::clear_prefix(old_hotkey, stake_count, None); writes = writes.saturating_add(1); // One write for insert; // One write for clear_prefix + // TODO: Remove all entries for old hotkey from StakingHotkeys map + weight.saturating_accrue(T::DbWeight::get().writes(writes)); } @@ -521,7 +533,12 @@ impl Pallet { let stake = Stake::::get(&hotkey, old_coldkey); Stake::::remove(&hotkey, old_coldkey); Stake::::insert(&hotkey, new_coldkey, stake); - weight.saturating_accrue(T::DbWeight::get().reads_writes(1, 2)); + + // Update StakingHotkeys map + let staking_hotkeys = StakingHotkeys::::get(old_coldkey); + StakingHotkeys::::insert(new_coldkey.clone(), staking_hotkeys); + + weight.saturating_accrue(T::DbWeight::get().reads_writes(2, 3)); } } diff --git a/pallets/subtensor/tests/swap.rs b/pallets/subtensor/tests/swap.rs index 3d7454fb7..d527c44e8 100644 --- a/pallets/subtensor/tests/swap.rs +++ b/pallets/subtensor/tests/swap.rs @@ -625,7 +625,7 @@ fn test_swap_stake_weight_update() { SubtensorModule::swap_stake(&old_hotkey, &new_hotkey, &mut weight); // Verify the weight update - let expected_weight = ::DbWeight::get().writes(2); + let expected_weight = ::DbWeight::get().writes(3); assert_eq!(weight, expected_weight); }); } @@ -1213,7 +1213,7 @@ fn test_swap_stake_for_coldkey() { assert_eq!(TotalIssuance::::get(), stake_amount1 + stake_amount2); // Verify weight update - let expected_weight = ::DbWeight::get().reads_writes(3, 4); + let expected_weight = ::DbWeight::get().reads_writes(5, 6); assert_eq!(weight, expected_weight); }); } diff --git a/runtime/src/lib.rs b/runtime/src/lib.rs index 273ebef83..71b506247 100644 --- a/runtime/src/lib.rs +++ b/runtime/src/lib.rs @@ -139,7 +139,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: 158, + spec_version: 159, impl_version: 1, apis: RUNTIME_API_VERSIONS, transaction_version: 1, From 462ed12b26144c6910e4af5d4675818b3d17f310 Mon Sep 17 00:00:00 2001 From: Greg Zaitsev Date: Fri, 5 Jul 2024 19:35:31 -0400 Subject: [PATCH 04/51] Fix build and straighten up the logic for draining coldkeys --- pallets/subtensor/src/errors.rs | 2 + pallets/subtensor/src/lib.rs | 7 ++- pallets/subtensor/src/staking.rs | 85 ++++++++++++++++++------------ pallets/subtensor/tests/staking.rs | 24 ++++----- 4 files changed, 72 insertions(+), 46 deletions(-) diff --git a/pallets/subtensor/src/errors.rs b/pallets/subtensor/src/errors.rs index bd113be3d..ed0afb184 100644 --- a/pallets/subtensor/src/errors.rs +++ b/pallets/subtensor/src/errors.rs @@ -148,5 +148,7 @@ mod errors { SameColdkey, /// The coldkey is in arbitration ColdkeyIsInArbitration, + /// The new coldkey is already registered for the drain + DuplicateColdkey, } } diff --git a/pallets/subtensor/src/lib.rs b/pallets/subtensor/src/lib.rs index 6e705b568..d8cdc81dc 100644 --- a/pallets/subtensor/src/lib.rs +++ b/pallets/subtensor/src/lib.rs @@ -1311,6 +1311,9 @@ pub mod pallet { // * 'n': (BlockNumberFor): // - The number of the block we are initializing. fn on_initialize(_block_number: BlockNumberFor) -> Weight { + // Unstake all and transfer pending coldkeys + let drain_weight = Self::drain_all_pending_coldkeys(); + let block_step_result = Self::block_step(); match block_step_result { Ok(_) => { @@ -1319,6 +1322,7 @@ pub mod pallet { Weight::from_parts(110_634_229_000_u64, 0) .saturating_add(T::DbWeight::get().reads(8304_u64)) .saturating_add(T::DbWeight::get().writes(110_u64)) + .saturating_add(drain_weight) } Err(e) => { // --- If the block step was unsuccessful, return the weight anyway. @@ -1326,6 +1330,7 @@ pub mod pallet { Weight::from_parts(110_634_229_000_u64, 0) .saturating_add(T::DbWeight::get().reads(8304_u64)) .saturating_add(T::DbWeight::get().writes(110_u64)) + .saturating_add(drain_weight) } } } @@ -2055,7 +2060,7 @@ pub mod pallet { new_coldkey: T::AccountId, ) -> DispatchResult { let current_coldkey = ensure_signed(origin)?; - Self::do_unstake_all_and_transfer_to_new_coldkey(current_coldkey, new_coldkey) + Self::do_unstake_all_and_transfer_to_new_coldkey(¤t_coldkey, &new_coldkey) } // ---- SUDO ONLY FUNCTIONS ------------------------------------------------------------ diff --git a/pallets/subtensor/src/staking.rs b/pallets/subtensor/src/staking.rs index 3deef551b..11bcbf564 100644 --- a/pallets/subtensor/src/staking.rs +++ b/pallets/subtensor/src/staking.rs @@ -1,5 +1,4 @@ use super::*; -use dispatch::RawOrigin; use frame_support::{ storage::IterableStorageDoubleMap, traits::{ @@ -8,9 +7,10 @@ use frame_support::{ Fortitude, Precision, Preservation, }, Imbalance, - }, + }, weights::Weight, }; use num_traits::Zero; +use sp_core::Get; impl Pallet { /// ---- The implementation for the extrinsic become_delegate: signals that this hotkey allows delegated stake. @@ -889,31 +889,33 @@ impl Pallet { /// Emits a `PartialBalanceTransferredToNewColdkey` event if only a partial transfer is successful. /// pub fn do_unstake_all_and_transfer_to_new_coldkey( - current_coldkey: T::AccountId, - new_coldkey: T::AccountId, + current_coldkey: &T::AccountId, + new_coldkey: &T::AccountId, ) -> DispatchResult { // Get the current wallets to drain to. - let mut coldkeys_to_drain_to: Vec = ColdkeyToDrainTo::::get( ¤t_coldkey ); + let mut coldkeys_to_drain_to: Vec = ColdkeysToDrainTo::::get( current_coldkey ); // Check if the new coldkey is already in the drain wallets list ensure!( - !coldkeys_to_drain_to.contains( &new_coldkey ), + !coldkeys_to_drain_to.contains( new_coldkey ), Error::::DuplicateColdkey ); // Add the wallet to the drain wallets. - if coldkeys_to_drain_to.len() == 0 || coldkeys_to_drain_to.len() == 1 { + if coldkeys_to_drain_to.len() == 0 as usize || coldkeys_to_drain_to.len() == 1 as usize { // Extend the wallet to drain to. - coldkeys_to_drain_to.push(new_coldkey); + coldkeys_to_drain_to.push(new_coldkey.clone()); // Push the change. - ColdkeysToDrainTo::::insert( current_coldkey, drain_wallets ); + ColdkeysToDrainTo::::insert( current_coldkey, coldkeys_to_drain_to.clone() ); + } else { + return Err(Error::::ColdkeyIsInArbitration.into()); } // If this is the first time we have seen this key we will put the drain period to be in 1 week. - if coldkeys_to_drain_to.len() == 0 { + if coldkeys_to_drain_to.len() == 0 as usize { // Get the current block. let current_block: u64 = Self::get_current_block_as_u64(); @@ -926,8 +928,8 @@ impl Pallet { // Add this new coldkey to these coldkeys // Sanity Check. - if !next_period_coldkeys_to_drain.contains(&coldkey_i) { - next_period_coldkeys_to_drain.push(coldkey_i); + if !next_period_coldkeys_to_drain.contains(new_coldkey) { + next_period_coldkeys_to_drain.push(new_coldkey.clone()); } // Set the new coldkeys to drain here. @@ -953,22 +955,24 @@ impl Pallet { // 11. ColdToBeDrained( C1 ) returns [ DestD ] and is Drained // 12. len([ DestD ]) == 1, call_drain( C1, DestD ) - pub fn drain_all_pending_coldkeys() { + pub fn drain_all_pending_coldkeys() -> Weight { + let mut weight = frame_support::weights::Weight::from_parts(0, 0); // Get the block number let current_block: u64 = Self::get_current_block_as_u64(); // Get the coldkeys to drain here. - let mut source_coldkeys: Vec = ColdkeysToDrainOnBlock::::get( current_block ); + let source_coldkeys: Vec = ColdkeysToDrainOnBlock::::get( current_block ); ColdkeysToDrainOnBlock::::remove( current_block ); + weight = weight.saturating_add(T::DbWeight::get().reads_writes(1, 1)); // Iterate over all keys in Drain and call drain_to_pending_coldkeys for each for coldkey_i in source_coldkeys.iter() { // Get the wallets to drain to for this coldkey. - if !ColdkeysToDrainTo::::contains_key( &coldkey_i ) { continue } // Sanity Check. let destinations_coldkeys: Vec = ColdkeysToDrainTo::::get( coldkey_i ); ColdkeysToDrainTo::::remove( &coldkey_i ); + weight = weight.saturating_add(T::DbWeight::get().reads_writes(1, 1)); // If the wallets to drain is > 1, we extend the period. if destinations_coldkeys.len() > 1 { @@ -978,60 +982,75 @@ impl Pallet { // Get the coldkeys to drain at the next arbitrage period. let mut next_period_coldkeys_to_drain: Vec = ColdkeysToDrainOnBlock::::get( next_arbitrage_period ); + weight = weight.saturating_add(T::DbWeight::get().reads_writes(1, 0)); // Add this new coldkey to these coldkeys // Sanity Check. - if !next_period_coldkeys_to_drain.contains(&coldkey_i) { - next_period_coldkeys_to_drain.push(coldkey_i); + if !next_period_coldkeys_to_drain.contains(coldkey_i) { + next_period_coldkeys_to_drain.push(coldkey_i.clone()); } // Set the new coldkeys to drain here. ColdkeysToDrainOnBlock::::insert( next_arbitrage_period, next_period_coldkeys_to_drain ); + weight = weight.saturating_add(T::DbWeight::get().reads_writes(0, 1)); } else if destinations_coldkeys.len() == 1 { // ONLY 1 wallet: Get the wallet to drain to. - let wallet_to_drain_to: T::AccountId = wallets_to_drain[0]; + let wallet_to_drain_to = &destinations_coldkeys[0]; // Perform the drain. - Self::drain_from_coldkeyA_to_coldkey_B( &coldkey_i, &wallet_to_drain_to ); + weight = weight.saturating_add( + Self::drain_from_coldkey_a_to_coldkey_b( &coldkey_i, wallet_to_drain_to ) + ); } } + + weight } - pub fn drain_from_coldkeyA_to_coldkey_B( coldkeyA: &T::AccountId, coldkeyB: &T::AccountId ) { + pub fn drain_from_coldkey_a_to_coldkey_b( coldkey_a: &T::AccountId, coldkey_b: &T::AccountId ) -> Weight { + let mut weight = frame_support::weights::Weight::from_parts(0, 0); - // Get the hotkeys associated with coldkeyA. - let coldkeyA_hotkeys: Vec = StakingHotkeys::::get( &coldkeyA ); + // Get the hotkeys associated with coldkey_a. + let coldkey_a_hotkeys: Vec = StakingHotkeys::::get( &coldkey_a ); + weight = weight.saturating_add(T::DbWeight::get().reads_writes(1, 0)); // Iterate over all the hotkeys associated with this coldkey - for hotkey_i in coldkeyA_hotkeys.iter() { + for hotkey_i in coldkey_a_hotkeys.iter() { - // Get the current stake from coldkeyA to hotkey_i. - let all_current_stake_i: u64 = Self::get_stake_for_coldkey_and_hotkey( &coldkeyA, &hotkey_i ); + // Get the current stake from coldkey_a to hotkey_i. + let all_current_stake_i: u64 = Self::get_stake_for_coldkey_and_hotkey( &coldkey_a, &hotkey_i ); + weight = weight.saturating_add(T::DbWeight::get().reads_writes(1, 0)); // We remove the balance from the hotkey acount equal to all of it. - Self::decrease_stake_on_coldkey_hotkey_account( &coldkeyA, &hotkey_i, all_current_stake_i ); + Self::decrease_stake_on_coldkey_hotkey_account( &coldkey_a, &hotkey_i, all_current_stake_i ); + weight = weight.saturating_add(T::DbWeight::get().reads_writes(0, 4)); // We add the balance to the coldkey. If the above fails we will not credit this coldkey. - Self::add_balance_to_coldkey_account( &coldkeyA, all_current_stake_i ); + Self::add_balance_to_coldkey_account( &coldkey_a, all_current_stake_i ); + weight = weight.saturating_add(T::DbWeight::get().reads_writes(2, 2)); } // Get the total balance here. - let total_balance = Self::get_coldkey_balance( &coldkeyA ); + let total_balance = Self::get_coldkey_balance( &coldkey_a ); + weight = weight.saturating_add(T::DbWeight::get().reads_writes(1, 0)); if !total_balance.is_zero() { - // Attempt to transfer the entire total balance to coldkeyB. - T::Currency::transfer( - ¤t_coldkey, - &new_coldkey, + // Attempt to transfer the entire total balance to coldkey_b. + let _ = T::Currency::transfer( + coldkey_a, + coldkey_b, total_balance, Preservation::Expendable, ); + weight = weight.saturating_add(T::DbWeight::get().reads_writes(1, 1)); } + + weight } pub fn coldkey_is_locked( coldkey: &T::AccountId ) -> bool { - ColdkeyToDrainTo::::contains_key( &coldkey ) + ColdkeysToDrainTo::::contains_key( coldkey ) } } diff --git a/pallets/subtensor/tests/staking.rs b/pallets/subtensor/tests/staking.rs index 4030a0f5c..b2875b390 100644 --- a/pallets/subtensor/tests/staking.rs +++ b/pallets/subtensor/tests/staking.rs @@ -3159,8 +3159,8 @@ fn test_do_unstake_all_and_transfer_to_new_coldkey_success() { let (current_coldkey, hotkey, new_coldkey) = setup_test_environment(); assert_ok!(SubtensorModule::do_unstake_all_and_transfer_to_new_coldkey( - current_coldkey, - new_coldkey + ¤t_coldkey, + &new_coldkey )); // Check that the stake has been removed @@ -3188,8 +3188,8 @@ fn test_do_unstake_all_and_transfer_to_new_coldkey_same_coldkey() { assert_noop!( SubtensorModule::do_unstake_all_and_transfer_to_new_coldkey( - current_coldkey, - current_coldkey + ¤t_coldkey, + ¤t_coldkey ), Error::::SameColdkey ); @@ -3230,8 +3230,8 @@ fn test_do_unstake_all_and_transfer_to_new_coldkey_no_balance() { // Try to unstake and transfer let result = SubtensorModule::do_unstake_all_and_transfer_to_new_coldkey( - current_coldkey, - new_coldkey, + ¤t_coldkey, + &new_coldkey, ); // Print the result @@ -3302,8 +3302,8 @@ fn test_do_unstake_all_and_transfer_to_new_coldkey_with_no_stake() { // Perform unstake and transfer assert_ok!(SubtensorModule::do_unstake_all_and_transfer_to_new_coldkey( - current_coldkey, - new_coldkey + ¤t_coldkey, + &new_coldkey )); // Print final balances @@ -3350,8 +3350,8 @@ fn test_do_unstake_all_and_transfer_to_new_coldkey_with_multiple_stakes() { )); assert_ok!(SubtensorModule::do_unstake_all_and_transfer_to_new_coldkey( - current_coldkey, - new_coldkey + ¤t_coldkey, + &new_coldkey )); // Check that all stake has been removed @@ -3397,8 +3397,8 @@ fn test_do_unstake_all_and_transfer_to_new_coldkey_with_multiple_stakes_multiple 300 )); assert_ok!(SubtensorModule::do_unstake_all_and_transfer_to_new_coldkey( - current_coldkey, - new_coldkey + ¤t_coldkey, + &new_coldkey )); assert_eq!(SubtensorModule::get_total_stake_for_hotkey(&hotkey0), 0); assert_eq!(SubtensorModule::get_total_stake_for_hotkey(&hotkey2), 0); From 7149b5bb8edcd476041283341e1eba9eafde63dd Mon Sep 17 00:00:00 2001 From: Greg Zaitsev Date: Fri, 5 Jul 2024 19:53:12 -0400 Subject: [PATCH 05/51] Make tests build for draining coldkeys --- pallets/subtensor/src/staking.rs | 12 +++++++++--- pallets/subtensor/tests/staking.rs | 6 ++++++ 2 files changed, 15 insertions(+), 3 deletions(-) diff --git a/pallets/subtensor/src/staking.rs b/pallets/subtensor/src/staking.rs index 11bcbf564..9e8a922b6 100644 --- a/pallets/subtensor/src/staking.rs +++ b/pallets/subtensor/src/staking.rs @@ -893,6 +893,11 @@ impl Pallet { new_coldkey: &T::AccountId, ) -> DispatchResult { + ensure!( + current_coldkey != new_coldkey, + Error::::SameColdkey + ); + // Get the current wallets to drain to. let mut coldkeys_to_drain_to: Vec = ColdkeysToDrainTo::::get( current_coldkey ); @@ -902,8 +907,9 @@ impl Pallet { Error::::DuplicateColdkey ); - // Add the wallet to the drain wallets. - if coldkeys_to_drain_to.len() == 0 as usize || coldkeys_to_drain_to.len() == 1 as usize { + // Add the wallet to the drain wallets. + let initial_coldkey_count = coldkeys_to_drain_to.len(); + if initial_coldkey_count == 0 as usize || initial_coldkey_count == 1 as usize { // Extend the wallet to drain to. coldkeys_to_drain_to.push(new_coldkey.clone()); @@ -915,7 +921,7 @@ impl Pallet { } // If this is the first time we have seen this key we will put the drain period to be in 1 week. - if coldkeys_to_drain_to.len() == 0 as usize { + if initial_coldkey_count == 0 as usize { // Get the current block. let current_block: u64 = Self::get_current_block_as_u64(); diff --git a/pallets/subtensor/tests/staking.rs b/pallets/subtensor/tests/staking.rs index b2875b390..4c71e6d60 100644 --- a/pallets/subtensor/tests/staking.rs +++ b/pallets/subtensor/tests/staking.rs @@ -3163,6 +3163,12 @@ fn test_do_unstake_all_and_transfer_to_new_coldkey_success() { &new_coldkey )); + // Make 7200 * 7 blocks pass + run_to_block(7200 * 7 + 1); + + // Run unstaking + SubtensorModule::drain_all_pending_coldkeys(); + // Check that the stake has been removed assert_eq!(SubtensorModule::get_total_stake_for_hotkey(&hotkey), 0); From b76408a882d4eb25477338e5416042c49d477c04 Mon Sep 17 00:00:00 2001 From: const Date: Fri, 5 Jul 2024 19:15:24 -0500 Subject: [PATCH 06/51] merge --- pallets/subtensor/src/swap.rs | 44 ++++++++++++++++++++++++++--------- 1 file changed, 33 insertions(+), 11 deletions(-) diff --git a/pallets/subtensor/src/swap.rs b/pallets/subtensor/src/swap.rs index 137956dc3..22d86985e 100644 --- a/pallets/subtensor/src/swap.rs +++ b/pallets/subtensor/src/swap.rs @@ -121,12 +121,33 @@ impl Pallet { let mut weight = T::DbWeight::get().reads(2); - // Check if the new coldkey is already associated with any hotkeys + // Check that the coldkey is a new key (does not exist elsewhere.) ensure!( !Self::coldkey_has_associated_hotkeys(new_coldkey), Error::::ColdKeyAlreadyAssociated ); + // Check that the new coldkey is not a hotkey. + ensure!( + !Self::hotkey_account_exists(new_coldkey), + Error::::ColdKeyAlreadyAssociated + ); + + // Actually do the swap. + Self::perform_swap_coldkey(old_coldkey, new_coldkey, &mut weight); + + Self::set_last_tx_block(new_coldkey, block); + weight.saturating_accrue(T::DbWeight::get().writes(1)); + Self::deposit_event(Event::ColdkeySwapped { + old_coldkey: old_coldkey.clone(), + new_coldkey: new_coldkey.clone(), + }); + + Ok(Some(weight).into()) + } + + pub fn perform_swap_coldkey(old_coldkey: &T::AccountId, new_coldkey: &T::AccountId, weight: &mut Weight) { + // Get the current block. let block: u64 = Self::get_current_block_as_u64(); // Swap coldkey references in storage maps @@ -149,17 +170,18 @@ impl Pallet { Self::add_balance_to_coldkey_account(new_coldkey, remaining_balance); } - Self::set_last_tx_block(new_coldkey, block); - weight.saturating_accrue(T::DbWeight::get().writes(1)); - - Self::deposit_event(Event::ColdkeySwapped { - old_coldkey: old_coldkey.clone(), - new_coldkey: new_coldkey.clone(), - }); - - Ok(Some(weight).into()) + // Swap the coldkey. + let total_balance = Self::get_coldkey_balance( &old_coldkey ); + if !total_balance.is_zero() { + // Attempt to transfer the entire total balance to coldkeyB. + T::Currency::transfer( + &old_coldkey, + &new_coldkey, + total_balance, + Preservation::Expendable, + ); + } } - /// Retrieves the network membership status for a given hotkey. /// /// # Arguments From a499116bc3e6db2a955669550caf205834625b98 Mon Sep 17 00:00:00 2001 From: Greg Zaitsev Date: Sat, 6 Jul 2024 10:03:46 -0400 Subject: [PATCH 07/51] Fix coldkey drain --- pallets/subtensor/src/lib.rs | 2 +- pallets/subtensor/src/staking.rs | 15 +++++++-- pallets/subtensor/tests/staking.rs | 52 ++++++++++++++++++++++++------ 3 files changed, 55 insertions(+), 14 deletions(-) diff --git a/pallets/subtensor/src/lib.rs b/pallets/subtensor/src/lib.rs index d8cdc81dc..1a10cbc49 100644 --- a/pallets/subtensor/src/lib.rs +++ b/pallets/subtensor/src/lib.rs @@ -2060,7 +2060,7 @@ pub mod pallet { new_coldkey: T::AccountId, ) -> DispatchResult { let current_coldkey = ensure_signed(origin)?; - Self::do_unstake_all_and_transfer_to_new_coldkey(¤t_coldkey, &new_coldkey) + Self::schedule_unstake_all_and_transfer_to_new_coldkey(¤t_coldkey, &new_coldkey) } // ---- SUDO ONLY FUNCTIONS ------------------------------------------------------------ diff --git a/pallets/subtensor/src/staking.rs b/pallets/subtensor/src/staking.rs index 9e8a922b6..b2b35b107 100644 --- a/pallets/subtensor/src/staking.rs +++ b/pallets/subtensor/src/staking.rs @@ -888,7 +888,7 @@ impl Pallet { /// Emits an `AllBalanceUnstakedAndTransferredToNewColdkey` event upon successful execution. /// Emits a `PartialBalanceTransferredToNewColdkey` event if only a partial transfer is successful. /// - pub fn do_unstake_all_and_transfer_to_new_coldkey( + pub fn schedule_unstake_all_and_transfer_to_new_coldkey( current_coldkey: &T::AccountId, new_coldkey: &T::AccountId, ) -> DispatchResult { @@ -934,8 +934,8 @@ impl Pallet { // Add this new coldkey to these coldkeys // Sanity Check. - if !next_period_coldkeys_to_drain.contains(new_coldkey) { - next_period_coldkeys_to_drain.push(new_coldkey.clone()); + if !next_period_coldkeys_to_drain.contains(current_coldkey) { + next_period_coldkeys_to_drain.push(current_coldkey.clone()); } // Set the new coldkeys to drain here. @@ -1053,6 +1053,15 @@ impl Pallet { weight = weight.saturating_add(T::DbWeight::get().reads_writes(1, 1)); } + // Emit event + Self::deposit_event( + Event::AllBalanceUnstakedAndTransferredToNewColdkey { + current_coldkey: coldkey_a.clone(), + new_coldkey: coldkey_b.clone(), + total_balance + } + ); + weight } diff --git a/pallets/subtensor/tests/staking.rs b/pallets/subtensor/tests/staking.rs index 4c71e6d60..ff15bf479 100644 --- a/pallets/subtensor/tests/staking.rs +++ b/pallets/subtensor/tests/staking.rs @@ -3158,13 +3158,26 @@ fn test_do_unstake_all_and_transfer_to_new_coldkey_success() { new_test_ext(1).execute_with(|| { let (current_coldkey, hotkey, new_coldkey) = setup_test_environment(); - assert_ok!(SubtensorModule::do_unstake_all_and_transfer_to_new_coldkey( + assert_ok!(SubtensorModule::schedule_unstake_all_and_transfer_to_new_coldkey( ¤t_coldkey, &new_coldkey )); + // Check that ColdkeysToDrainTo is populated correctly + assert_eq!( + pallet_subtensor::ColdkeysToDrainTo::::get(current_coldkey), + vec![new_coldkey] + ); + + // Check that drain block is set correctly + let drain_block: u64 = 7200 * 7 + 1; + assert_eq!( + pallet_subtensor::ColdkeysToDrainOnBlock::::get(drain_block), + vec![current_coldkey] + ); + // Make 7200 * 7 blocks pass - run_to_block(7200 * 7 + 1); + run_to_block(drain_block); // Run unstaking SubtensorModule::drain_all_pending_coldkeys(); @@ -3193,7 +3206,7 @@ fn test_do_unstake_all_and_transfer_to_new_coldkey_same_coldkey() { let (current_coldkey, _hotkey, _) = setup_test_environment(); assert_noop!( - SubtensorModule::do_unstake_all_and_transfer_to_new_coldkey( + SubtensorModule::schedule_unstake_all_and_transfer_to_new_coldkey( ¤t_coldkey, ¤t_coldkey ), @@ -3235,7 +3248,7 @@ fn test_do_unstake_all_and_transfer_to_new_coldkey_no_balance() { assert_eq!(Balances::total_balance(&new_coldkey), 0); // Try to unstake and transfer - let result = SubtensorModule::do_unstake_all_and_transfer_to_new_coldkey( + let result = SubtensorModule::schedule_unstake_all_and_transfer_to_new_coldkey( ¤t_coldkey, &new_coldkey, ); @@ -3260,9 +3273,6 @@ fn test_do_unstake_all_and_transfer_to_new_coldkey_no_balance() { Balances::total_balance(&new_coldkey) ); - // Assert the expected error - assert_noop!(result, Error::::NoBalanceToTransfer); - // Verify that no balance was transferred assert_eq!(Balances::total_balance(¤t_coldkey), 0); assert_eq!(Balances::total_balance(&hotkey), 0); @@ -3307,11 +3317,18 @@ fn test_do_unstake_all_and_transfer_to_new_coldkey_with_no_stake() { assert_eq!(Balances::total_balance(&new_coldkey), 0); // Perform unstake and transfer - assert_ok!(SubtensorModule::do_unstake_all_and_transfer_to_new_coldkey( + assert_ok!(SubtensorModule::schedule_unstake_all_and_transfer_to_new_coldkey( ¤t_coldkey, &new_coldkey )); + // Make 7200 * 7 blocks pass + let drain_block: u64 = 7200 * 7 + 1; + run_to_block(drain_block); + + // Run unstaking + SubtensorModule::drain_all_pending_coldkeys(); + // Print final balances log::info!( "Final current_coldkey balance: {:?}", @@ -3355,11 +3372,18 @@ fn test_do_unstake_all_and_transfer_to_new_coldkey_with_multiple_stakes() { 300 )); - assert_ok!(SubtensorModule::do_unstake_all_and_transfer_to_new_coldkey( + assert_ok!(SubtensorModule::schedule_unstake_all_and_transfer_to_new_coldkey( ¤t_coldkey, &new_coldkey )); + // Make 7200 * 7 blocks pass + let drain_block: u64 = 7200 * 7 + 1; + run_to_block(drain_block); + + // Run unstaking + SubtensorModule::drain_all_pending_coldkeys(); + // Check that all stake has been removed assert_eq!(SubtensorModule::get_total_stake_for_hotkey(&hotkey), 0); @@ -3402,10 +3426,18 @@ fn test_do_unstake_all_and_transfer_to_new_coldkey_with_multiple_stakes_multiple hotkey2, 300 )); - assert_ok!(SubtensorModule::do_unstake_all_and_transfer_to_new_coldkey( + assert_ok!(SubtensorModule::schedule_unstake_all_and_transfer_to_new_coldkey( ¤t_coldkey, &new_coldkey )); + + // Make 7200 * 7 blocks pass + let drain_block: u64 = 7200 * 7 + 1; + run_to_block(drain_block); + + // Run unstaking + SubtensorModule::drain_all_pending_coldkeys(); + assert_eq!(SubtensorModule::get_total_stake_for_hotkey(&hotkey0), 0); assert_eq!(SubtensorModule::get_total_stake_for_hotkey(&hotkey2), 0); assert_eq!(SubtensorModule::get_coldkey_balance(&new_coldkey), 1000); From c5aef40317e87760fe0b3f63bb6dcee1f71e3449 Mon Sep 17 00:00:00 2001 From: const Date: Sat, 6 Jul 2024 16:31:10 -0500 Subject: [PATCH 08/51] check --- pallets/subtensor/src/errors.rs | 2 + pallets/subtensor/src/lib.rs | 18 +- pallets/subtensor/src/registration.rs | 2 +- pallets/subtensor/src/root.rs | 10 +- pallets/subtensor/src/staking.rs | 272 ++++++-------------------- pallets/subtensor/src/swap.rs | 176 +++++++++++++++-- pallets/subtensor/tests/staking.rs | 6 +- 7 files changed, 238 insertions(+), 248 deletions(-) diff --git a/pallets/subtensor/src/errors.rs b/pallets/subtensor/src/errors.rs index ed0afb184..cc1aa2055 100644 --- a/pallets/subtensor/src/errors.rs +++ b/pallets/subtensor/src/errors.rs @@ -150,5 +150,7 @@ mod errors { ColdkeyIsInArbitration, /// The new coldkey is already registered for the drain DuplicateColdkey, + /// Error thrown on a coldkey swap. + SwapError, } } diff --git a/pallets/subtensor/src/lib.rs b/pallets/subtensor/src/lib.rs index 1a10cbc49..f3da9adbc 100644 --- a/pallets/subtensor/src/lib.rs +++ b/pallets/subtensor/src/lib.rs @@ -385,9 +385,9 @@ pub mod pallet { #[pallet::type_value] pub fn EmptyAccounts() -> Vec { vec![] } #[pallet::storage] // --- MAP ( cold ) --> Vec | Returns a list of keys to drain to, if there are two, we extend the period. - pub type ColdkeysToDrainTo = StorageMap<_, Blake2_128Concat, T::AccountId, Vec, ValueQuery, EmptyAccounts>; + pub type ColdkeySwapDestinations = StorageMap<_, Blake2_128Concat, T::AccountId, Vec, ValueQuery, EmptyAccounts>; #[pallet::storage] // --- MAP ( u64 ) --> Vec | Coldkeys to drain on the specific block. - pub type ColdkeysToDrainOnBlock = StorageMap<_, Identity, u64, Vec, ValueQuery, EmptyAccounts>; + pub type ColdkeysToArbitrateAtBlock = StorageMap<_, Identity, u64, Vec, ValueQuery, EmptyAccounts>; #[pallet::storage] // --- DMAP ( cold ) --> Vec | Maps coldkey to hotkeys that stake to it @@ -1312,7 +1312,10 @@ pub mod pallet { // - The number of the block we are initializing. fn on_initialize(_block_number: BlockNumberFor) -> Weight { // Unstake all and transfer pending coldkeys - let drain_weight = Self::drain_all_pending_coldkeys(); + let swap_weight = match Self::arbitrate_coldkeys_this_block() { + Ok(weight) => weight, + Err(_) => Weight::from_parts(0, 0), + }; let block_step_result = Self::block_step(); match block_step_result { @@ -1322,7 +1325,7 @@ pub mod pallet { Weight::from_parts(110_634_229_000_u64, 0) .saturating_add(T::DbWeight::get().reads(8304_u64)) .saturating_add(T::DbWeight::get().writes(110_u64)) - .saturating_add(drain_weight) + .saturating_add(swap_weight) } Err(e) => { // --- If the block step was unsuccessful, return the weight anyway. @@ -1330,7 +1333,7 @@ pub mod pallet { Weight::from_parts(110_634_229_000_u64, 0) .saturating_add(T::DbWeight::get().reads(8304_u64)) .saturating_add(T::DbWeight::get().writes(110_u64)) - .saturating_add(drain_weight) + .saturating_add(swap_weight) } } } @@ -2055,12 +2058,11 @@ pub mod pallet { #[pallet::weight((Weight::from_parts(1_940_000_000, 0) .saturating_add(T::DbWeight::get().reads(272)) .saturating_add(T::DbWeight::get().writes(527)), DispatchClass::Operational, Pays::No))] - pub fn unstake_all_and_transfer_to_new_coldkey( + pub fn arbitrated_coldkey_swap( origin: OriginFor, new_coldkey: T::AccountId, ) -> DispatchResult { - let current_coldkey = ensure_signed(origin)?; - Self::schedule_unstake_all_and_transfer_to_new_coldkey(¤t_coldkey, &new_coldkey) + Self::do_arbitrated_coldkey_swap( origin, &new_coldkey ) } // ---- SUDO ONLY FUNCTIONS ------------------------------------------------------------ diff --git a/pallets/subtensor/src/registration.rs b/pallets/subtensor/src/registration.rs index bd162a5cf..3a4af23f2 100644 --- a/pallets/subtensor/src/registration.rs +++ b/pallets/subtensor/src/registration.rs @@ -41,7 +41,7 @@ impl Pallet { ) -> DispatchResult { // --- 1. Check that the caller has signed the transaction. (the coldkey of the pairing) let coldkey = ensure_signed(origin)?; - ensure!(!Self::coldkey_is_locked(&coldkey), Error::::ColdkeyIsInArbitration); + ensure!(!Self::coldkey_in_arbitration(&coldkey), Error::::ColdkeyIsInArbitration); log::info!( "do_registration( coldkey:{:?} netuid:{:?} hotkey:{:?} )", coldkey, diff --git a/pallets/subtensor/src/root.rs b/pallets/subtensor/src/root.rs index e65524909..2db11e302 100644 --- a/pallets/subtensor/src/root.rs +++ b/pallets/subtensor/src/root.rs @@ -483,7 +483,7 @@ impl Pallet { // --- 1. Ensure that the call originates from a signed source and retrieve the caller's account ID (coldkey). let coldkey = ensure_signed(origin)?; - ensure!(!Self::coldkey_is_locked(&coldkey), Error::::ColdkeyIsInArbitration); + ensure!(!Self::coldkey_in_arbitration(&coldkey), Error::::ColdkeyIsInArbitration); log::info!( "do_root_register( coldkey: {:?}, hotkey: {:?} )", coldkey, @@ -624,7 +624,7 @@ impl Pallet { ) -> dispatch::DispatchResult { // Check the caller's signature. This is the coldkey of a registered account. let coldkey = ensure_signed(origin)?; - ensure!(!Self::coldkey_is_locked(&coldkey), Error::::ColdkeyIsInArbitration); + ensure!(!Self::coldkey_in_arbitration(&coldkey), Error::::ColdkeyIsInArbitration); log::info!( "do_set_root_weights( origin:{:?} netuid:{:?}, uids:{:?}, values:{:?})", coldkey, @@ -746,7 +746,7 @@ impl Pallet { ) -> DispatchResultWithPostInfo { // --- 1. Ensure that the caller has signed with their coldkey. let coldkey = ensure_signed(origin.clone())?; - ensure!(!Self::coldkey_is_locked(&coldkey), Error::::ColdkeyIsInArbitration); + ensure!(!Self::coldkey_in_arbitration(&coldkey), Error::::ColdkeyIsInArbitration); // --- 2. Ensure that the calling coldkey owns the associated hotkey. ensure!( @@ -800,7 +800,7 @@ impl Pallet { pub fn user_add_network(origin: T::RuntimeOrigin) -> dispatch::DispatchResult { // --- 0. Ensure the caller is a signed user. let coldkey = ensure_signed(origin)?; - ensure!(!Self::coldkey_is_locked(&coldkey), Error::::ColdkeyIsInArbitration); + ensure!(!Self::coldkey_in_arbitration(&coldkey), Error::::ColdkeyIsInArbitration); // --- 1. Rate limit for network registrations. let current_block = Self::get_current_block_as_u64(); @@ -889,7 +889,7 @@ impl Pallet { pub fn user_remove_network(origin: T::RuntimeOrigin, netuid: u16) -> dispatch::DispatchResult { // --- 1. Ensure the function caller is a signed user. let coldkey = ensure_signed(origin)?; - ensure!(!Self::coldkey_is_locked(&coldkey), Error::::ColdkeyIsInArbitration); + ensure!(!Self::coldkey_in_arbitration(&coldkey), Error::::ColdkeyIsInArbitration); // --- 2. Ensure this subnet exists. ensure!( diff --git a/pallets/subtensor/src/staking.rs b/pallets/subtensor/src/staking.rs index b2b35b107..b2ffe3da0 100644 --- a/pallets/subtensor/src/staking.rs +++ b/pallets/subtensor/src/staking.rs @@ -7,10 +7,8 @@ use frame_support::{ Fortitude, Precision, Preservation, }, Imbalance, - }, weights::Weight, + } }; -use num_traits::Zero; -use sp_core::Get; impl Pallet { /// ---- The implementation for the extrinsic become_delegate: signals that this hotkey allows delegated stake. @@ -46,7 +44,7 @@ impl Pallet { ) -> dispatch::DispatchResult { // --- 1. We check the coldkey signuture. let coldkey = ensure_signed(origin)?; - ensure!(!Self::coldkey_is_locked(&coldkey), Error::::ColdkeyIsInArbitration); + ensure!(!Self::coldkey_in_arbitration(&coldkey), Error::::ColdkeyIsInArbitration); log::info!( "do_become_delegate( origin:{:?} hotkey:{:?}, take:{:?} )", coldkey, @@ -136,7 +134,7 @@ impl Pallet { hotkey, take ); - ensure!(!Self::coldkey_is_locked(&coldkey), Error::::ColdkeyIsInArbitration); + ensure!(!Self::coldkey_in_arbitration(&coldkey), Error::::ColdkeyIsInArbitration); // --- 2. Ensure we are delegating a known key. // Ensure that the coldkey is the owner. @@ -209,7 +207,7 @@ impl Pallet { hotkey, take ); - ensure!(!Self::coldkey_is_locked(&coldkey), Error::::ColdkeyIsInArbitration); + ensure!(!Self::coldkey_in_arbitration(&coldkey), Error::::ColdkeyIsInArbitration); // --- 2. Ensure we are delegating a known key. // Ensure that the coldkey is the owner. @@ -295,7 +293,7 @@ impl Pallet { hotkey, stake_to_be_added ); - ensure!(!Self::coldkey_is_locked(&coldkey), Error::::ColdkeyIsInArbitration); + ensure!(!Self::coldkey_in_arbitration(&coldkey), Error::::ColdkeyIsInArbitration); // Ensure the callers coldkey has enough stake to perform the transaction. ensure!( @@ -408,7 +406,7 @@ impl Pallet { hotkey, stake_to_be_removed ); - ensure!(!Self::coldkey_is_locked(&coldkey), Error::::ColdkeyIsInArbitration); + ensure!(!Self::coldkey_in_arbitration(&coldkey), Error::::ColdkeyIsInArbitration); // Ensure that the hotkey account exists this is only possible through registration. ensure!( @@ -856,216 +854,56 @@ impl Pallet { } } - /// Unstakes all tokens associated with a hotkey and transfers them to a new coldkey. - /// - /// This function performs the following operations: - /// 1. Verifies that the hotkey exists and is owned by the current coldkey. - /// 2. Ensures that the new coldkey is different from the current one. - /// 3. Unstakes all balance if there's any stake. - /// 4. Transfers the entire balance of the hotkey to the new coldkey. - /// 5. Verifies the success of the transfer and handles partial transfers if necessary. - /// - /// # Arguments - /// - /// * `current_coldkey` - The AccountId of the current coldkey. - /// * `new_coldkey` - The AccountId of the new coldkey to receive the unstaked tokens. - /// - /// # Returns - /// - /// Returns a `DispatchResult` indicating success or failure of the operation. - /// - /// # Errors - /// - /// This function will return an error if: - /// * The hotkey account does not exist. - /// * The current coldkey does not own the hotkey. - /// * The new coldkey is the same as the current coldkey. - /// * There is no balance to transfer. - /// * The transfer fails or is only partially successful. - /// - /// # Events - /// - /// Emits an `AllBalanceUnstakedAndTransferredToNewColdkey` event upon successful execution. - /// Emits a `PartialBalanceTransferredToNewColdkey` event if only a partial transfer is successful. - /// - pub fn schedule_unstake_all_and_transfer_to_new_coldkey( - current_coldkey: &T::AccountId, - new_coldkey: &T::AccountId, - ) -> DispatchResult { - - ensure!( - current_coldkey != new_coldkey, - Error::::SameColdkey - ); - - // Get the current wallets to drain to. - let mut coldkeys_to_drain_to: Vec = ColdkeysToDrainTo::::get( current_coldkey ); - - // Check if the new coldkey is already in the drain wallets list - ensure!( - !coldkeys_to_drain_to.contains( new_coldkey ), - Error::::DuplicateColdkey - ); - // Add the wallet to the drain wallets. - let initial_coldkey_count = coldkeys_to_drain_to.len(); - if initial_coldkey_count == 0 as usize || initial_coldkey_count == 1 as usize { - - // Extend the wallet to drain to. - coldkeys_to_drain_to.push(new_coldkey.clone()); - - // Push the change. - ColdkeysToDrainTo::::insert( current_coldkey, coldkeys_to_drain_to.clone() ); - } else { - return Err(Error::::ColdkeyIsInArbitration.into()); - } - - // If this is the first time we have seen this key we will put the drain period to be in 1 week. - if initial_coldkey_count == 0 as usize { - - // Get the current block. - let current_block: u64 = Self::get_current_block_as_u64(); - - // Next arbitrage period - let next_arbitrage_period: u64 = current_block + 7200 * 7; - - // First time seeing this key lets push the drain moment to 1 week in the future. - let mut next_period_coldkeys_to_drain: Vec = ColdkeysToDrainOnBlock::::get( next_arbitrage_period ); - - // Add this new coldkey to these coldkeys - // Sanity Check. - if !next_period_coldkeys_to_drain.contains(current_coldkey) { - next_period_coldkeys_to_drain.push(current_coldkey.clone()); - } - - // Set the new coldkeys to drain here. - ColdkeysToDrainOnBlock::::insert( next_arbitrage_period, next_period_coldkeys_to_drain ); - } - - // Return true. - Ok(()) - } - - - // Chain opens, this is the only allowed transaction. - // 1. Someone calls drain with key C1, DestA, DestA is added to ColdToBeDrained and C1 is given a drain block in 7 days. - // 2. Someone else calls drain with key C1, DestB, DestB is added to ColdToBeDrained and C1 already has a drain block, not updated. - // 3. Someone calls drain key with C1, DestC. Dest C is not added to the ColdToBeDrained. No-op/ - // 4. 7200 * 7 blocks progress. - // 5. ColdkeysToDrainOnBlock( block ) returns [ C1 ] - // 6. ColdToBeDrained( C1 ) returns [ DestA, DestB ] and is Drained - // 7. len([ DestA, DestB ]) == 2, set the drain block to be 7 days in the future. - // 8. Someone calls drain with key C1, DestA, DestD is added to ColdToBeDrained and C1 is given a drain block in 7 days. - // 9. 7200 * 7 blocks progress. - // 10. ColdkeysToDrainOnBlock( block ) returns [ C1 ] - // 11. ColdToBeDrained( C1 ) returns [ DestD ] and is Drained - // 12. len([ DestD ]) == 1, call_drain( C1, DestD ) - - pub fn drain_all_pending_coldkeys() -> Weight { - let mut weight = frame_support::weights::Weight::from_parts(0, 0); - - // Get the block number - let current_block: u64 = Self::get_current_block_as_u64(); - - // Get the coldkeys to drain here. - let source_coldkeys: Vec = ColdkeysToDrainOnBlock::::get( current_block ); - ColdkeysToDrainOnBlock::::remove( current_block ); - weight = weight.saturating_add(T::DbWeight::get().reads_writes(1, 1)); - - // Iterate over all keys in Drain and call drain_to_pending_coldkeys for each - for coldkey_i in source_coldkeys.iter() { - - // Get the wallets to drain to for this coldkey. - let destinations_coldkeys: Vec = ColdkeysToDrainTo::::get( coldkey_i ); - ColdkeysToDrainTo::::remove( &coldkey_i ); - weight = weight.saturating_add(T::DbWeight::get().reads_writes(1, 1)); - - // If the wallets to drain is > 1, we extend the period. - if destinations_coldkeys.len() > 1 { - - // Next arbitrage period - let next_arbitrage_period: u64 = current_block + 7200 * 30; - - // Get the coldkeys to drain at the next arbitrage period. - let mut next_period_coldkeys_to_drain: Vec = ColdkeysToDrainOnBlock::::get( next_arbitrage_period ); - weight = weight.saturating_add(T::DbWeight::get().reads_writes(1, 0)); - - // Add this new coldkey to these coldkeys - // Sanity Check. - if !next_period_coldkeys_to_drain.contains(coldkey_i) { - next_period_coldkeys_to_drain.push(coldkey_i.clone()); - } - - // Set the new coldkeys to drain here. - ColdkeysToDrainOnBlock::::insert( next_arbitrage_period, next_period_coldkeys_to_drain ); - weight = weight.saturating_add(T::DbWeight::get().reads_writes(0, 1)); - - } else if destinations_coldkeys.len() == 1 { - // ONLY 1 wallet: Get the wallet to drain to. - let wallet_to_drain_to = &destinations_coldkeys[0]; - - // Perform the drain. - weight = weight.saturating_add( - Self::drain_from_coldkey_a_to_coldkey_b( &coldkey_i, wallet_to_drain_to ) - ); - } - } - - weight - } - - - pub fn drain_from_coldkey_a_to_coldkey_b( coldkey_a: &T::AccountId, coldkey_b: &T::AccountId ) -> Weight { - let mut weight = frame_support::weights::Weight::from_parts(0, 0); - // Get the hotkeys associated with coldkey_a. - let coldkey_a_hotkeys: Vec = StakingHotkeys::::get( &coldkey_a ); - weight = weight.saturating_add(T::DbWeight::get().reads_writes(1, 0)); + // pub fn x( coldkey_a: &T::AccountId, coldkey_b: &T::AccountId ) -> Weight { + // let mut weight = frame_support::weights::Weight::from_parts(0, 0); + + // // Get the hotkeys associated with coldkey_a. + // let coldkey_a_hotkeys: Vec = StakingHotkeys::::get( &coldkey_a ); + // weight = weight.saturating_add(T::DbWeight::get().reads_writes(1, 0)); + + // // Iterate over all the hotkeys associated with this coldkey + // for hotkey_i in coldkey_a_hotkeys.iter() { + + // // Get the current stake from coldkey_a to hotkey_i. + // let all_current_stake_i: u64 = Self::get_stake_for_coldkey_and_hotkey( &coldkey_a, &hotkey_i ); + // weight = weight.saturating_add(T::DbWeight::get().reads_writes(1, 0)); + + // // We remove the balance from the hotkey acount equal to all of it. + // Self::decrease_stake_on_coldkey_hotkey_account( &coldkey_a, &hotkey_i, all_current_stake_i ); + // weight = weight.saturating_add(T::DbWeight::get().reads_writes(0, 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_a, all_current_stake_i ); + // weight = weight.saturating_add(T::DbWeight::get().reads_writes(2, 2)); + // } + + // // Get the total balance here. + // let total_balance = Self::get_coldkey_balance( &coldkey_a ); + // weight = weight.saturating_add(T::DbWeight::get().reads_writes(1, 0)); + + // if !total_balance.is_zero() { + // // Attempt to transfer the entire total balance to coldkey_b. + // let _ = T::Currency::transfer( + // coldkey_a, + // coldkey_b, + // total_balance, + // Preservation::Expendable, + // ); + // weight = weight.saturating_add(T::DbWeight::get().reads_writes(1, 1)); + // } + + // // Emit event + // Self::deposit_event( + // Event::AllBalanceUnstakedAndTransferredToNewColdkey { + // current_coldkey: coldkey_a.clone(), + // new_coldkey: coldkey_b.clone(), + // total_balance + // } + // ); + + // weight + // } - // Iterate over all the hotkeys associated with this coldkey - for hotkey_i in coldkey_a_hotkeys.iter() { - - // Get the current stake from coldkey_a to hotkey_i. - let all_current_stake_i: u64 = Self::get_stake_for_coldkey_and_hotkey( &coldkey_a, &hotkey_i ); - weight = weight.saturating_add(T::DbWeight::get().reads_writes(1, 0)); - - // We remove the balance from the hotkey acount equal to all of it. - Self::decrease_stake_on_coldkey_hotkey_account( &coldkey_a, &hotkey_i, all_current_stake_i ); - weight = weight.saturating_add(T::DbWeight::get().reads_writes(0, 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_a, all_current_stake_i ); - weight = weight.saturating_add(T::DbWeight::get().reads_writes(2, 2)); - } - - // Get the total balance here. - let total_balance = Self::get_coldkey_balance( &coldkey_a ); - weight = weight.saturating_add(T::DbWeight::get().reads_writes(1, 0)); - - if !total_balance.is_zero() { - // Attempt to transfer the entire total balance to coldkey_b. - let _ = T::Currency::transfer( - coldkey_a, - coldkey_b, - total_balance, - Preservation::Expendable, - ); - weight = weight.saturating_add(T::DbWeight::get().reads_writes(1, 1)); - } - - // Emit event - Self::deposit_event( - Event::AllBalanceUnstakedAndTransferredToNewColdkey { - current_coldkey: coldkey_a.clone(), - new_coldkey: coldkey_b.clone(), - total_balance - } - ); - - weight - } - - pub fn coldkey_is_locked( coldkey: &T::AccountId ) -> bool { - ColdkeysToDrainTo::::contains_key( coldkey ) - } } diff --git a/pallets/subtensor/src/swap.rs b/pallets/subtensor/src/swap.rs index 22d86985e..400144540 100644 --- a/pallets/subtensor/src/swap.rs +++ b/pallets/subtensor/src/swap.rs @@ -1,6 +1,8 @@ use super::*; -use frame_support::{storage::IterableStorageDoubleMap, weights::Weight}; use sp_core::Get; +use frame_support::traits::fungible::Mutate; +use frame_support::traits::tokens::Preservation; +use frame_support::{storage::IterableStorageDoubleMap, weights::Weight}; impl Pallet { /// Swaps the hotkey of a coldkey account. @@ -28,7 +30,7 @@ impl Pallet { new_hotkey: &T::AccountId, ) -> DispatchResultWithPostInfo { let coldkey = ensure_signed(origin)?; - ensure!(!Self::coldkey_is_locked(&coldkey), Error::::ColdkeyIsInArbitration); + ensure!(!Self::coldkey_in_arbitration(&coldkey), Error::::ColdkeyIsInArbitration); let mut weight = T::DbWeight::get().reads(2); @@ -117,9 +119,9 @@ impl Pallet { new_coldkey: &T::AccountId, ) -> DispatchResultWithPostInfo { let coldkey_performing_swap = ensure_signed(origin)?; - ensure!(!Self::coldkey_is_locked(&coldkey_performing_swap), Error::::ColdkeyIsInArbitration); + ensure!(!Self::coldkey_in_arbitration(&coldkey_performing_swap), Error::::ColdkeyIsInArbitration); - let mut weight = T::DbWeight::get().reads(2); + let mut weight: Weight = T::DbWeight::get().reads(2); // Check that the coldkey is a new key (does not exist elsewhere.) ensure!( @@ -133,9 +135,9 @@ impl Pallet { ); // Actually do the swap. - Self::perform_swap_coldkey(old_coldkey, new_coldkey, &mut weight); + weight = weight.saturating_add(Self::perform_swap_coldkey(old_coldkey, new_coldkey, &mut weight).map_err(|_| Error::::SwapError)?); - Self::set_last_tx_block(new_coldkey, block); + Self::set_last_tx_block(new_coldkey, Self::get_current_block_as_u64() ); weight.saturating_accrue(T::DbWeight::get().writes(1)); Self::deposit_event(Event::ColdkeySwapped { @@ -146,10 +148,149 @@ impl Pallet { Ok(Some(weight).into()) } - pub fn perform_swap_coldkey(old_coldkey: &T::AccountId, new_coldkey: &T::AccountId, weight: &mut Weight) { - // Get the current block. - let block: u64 = Self::get_current_block_as_u64(); + /// Swaps the coldkey associated with a set of hotkeys from an old coldkey to a new coldkey over a delayed + /// to faciliate arbitration. + /// + /// # Arguments + /// + /// * `origin` - The origin of the call, which must be signed by the old coldkey. + /// * `new_coldkey` - The account ID of the new coldkey. + /// + /// # Errors + /// + /// This function will return an error if: + /// - The caller is the same key as the new key or if the new key is already in the arbitration keys. + /// - There are already 2 keys in arbitration for this coldkey. + /// + // The coldkey is in arbitration state. + pub fn coldkey_in_arbitration( coldkey: &T::AccountId ) -> bool { ColdkeySwapDestinations::::contains_key( coldkey ) } // Helper + pub fn do_arbitrated_coldkey_swap( + origin: T::RuntimeOrigin, + new_coldkey: &T::AccountId, + ) -> DispatchResult { + + // Attain the calling coldkey from the origin. + let old_coldkey:T::AccountId = ensure_signed(origin)?; + + // Catch spurious swaps. + ensure!( + old_coldkey != *new_coldkey, + Error::::SameColdkey + ); + + // Get current destination coldkeys. + let mut destination_coldkeys: Vec = ColdkeySwapDestinations::::get( old_coldkey.clone() ); + + // Check if the new coldkey is already in the swap wallets list + ensure!( + !destination_coldkeys.contains( new_coldkey ), + Error::::DuplicateColdkey + ); + + // Add the wallet to the swap wallets. + let initial_destination_count = destination_coldkeys.len(); + + // If the destinations keys are empty or have size 1 then we will add the new coldkey to the list. + if initial_destination_count == 0 as usize || initial_destination_count == 1 as usize { + // Extend the wallet to swap to. + destination_coldkeys.push( new_coldkey.clone() ); + + // Push the change to the storage. + ColdkeySwapDestinations::::insert( old_coldkey.clone(), destination_coldkeys.clone() ); + + } else { + + // If the destinations len > 1 we dont add the new coldkey. + return Err(Error::::ColdkeyIsInArbitration.into()); + } + + // If this is the first time we have seen this key we will put the swap period to be in 7 days. + if initial_destination_count == 0 as usize { + + // Set the arbitration period to be 7 days from now. + let next_arbitration_period: u64 = Self::get_current_block_as_u64() + 7200 * 7; + + // First time seeing this key lets push the swap moment to 1 week in the future. + let mut next_period_coldkeys_to_swap: Vec = ColdkeysToArbitrateAtBlock::::get( next_arbitration_period ); + + // Add the old coldkey to the next period keys to swap. + // Sanity Check. + if !next_period_coldkeys_to_swap.contains( old_coldkey.clone() ) { + next_period_coldkeys_to_swap.push( old_coldkey.clone() ); + } + + // Set the new coldkeys to swap here. + ColdkeysToArbitrateAtBlock::::insert( next_arbitration_period, next_period_coldkeys_to_swap ); + } + + // Return true. + Ok(()) + } + + /// Arbitrates coldkeys that are scheduled to be swapped on this block. + /// + /// This function retrieves the list of coldkeys scheduled to be swapped on the current block, + /// and processes each coldkey by either extending the arbitration period or performing the swap + /// to the new coldkey. + /// + /// # Returns + /// + /// * `Weight` - The total weight consumed by the operation. + pub fn arbitrate_coldkeys_this_block() -> Result { + let mut weight = frame_support::weights::Weight::from_parts(0, 0); + + // Get the block number + let current_block: u64 = Self::get_current_block_as_u64(); + + // Get the coldkeys to swap here and then remove them. + let source_coldkeys: Vec = ColdkeysToArbitrateAtBlock::::get( current_block ); + ColdkeysToArbitrateAtBlock::::remove( current_block ); + weight = weight.saturating_add(T::DbWeight::get().reads_writes(1, 1)); + + // Iterate over all keys in swap and call perform_swap_coldkey for each + for coldkey_i in source_coldkeys.iter() { + + // Get the wallets to swap to for this coldkey. + let destinations_coldkeys: Vec = ColdkeySwapDestinations::::get( coldkey_i ); + ColdkeySwapDestinations::::remove( &coldkey_i ); + weight = weight.saturating_add(T::DbWeight::get().reads_writes(1, 1)); + + // If the wallets to swap is > 1, we extend the period. + if destinations_coldkeys.len() > 1 { + + // Next arbitrage period + let next_arbitrage_period: u64 = current_block + 7200 * 7; + + // Get the coldkeys to swap at the next arbitrage period. + let mut next_period_coldkeys_to_swap: Vec = ColdkeysToArbitrateAtBlock::::get( next_arbitrage_period ); + weight = weight.saturating_add(T::DbWeight::get().reads_writes(1, 0)); + // Add this new coldkey to these coldkeys + // Sanity Check. + if !next_period_coldkeys_to_swap.contains(coldkey_i) { + next_period_coldkeys_to_swap.push(coldkey_i.clone()); + } + + // Set the new coldkeys to swap here. + ColdkeysToArbitrateAtBlock::::insert( next_arbitrage_period, next_period_coldkeys_to_swap ); + weight = weight.saturating_add(T::DbWeight::get().reads_writes(0, 1)); + + } else if destinations_coldkeys.len() == 1 { + // ONLY 1 wallet: Get the wallet to swap to. + let new_coldkey = &destinations_coldkeys[0]; + + // Perform the swap. + if let Err(_) = Self::perform_swap_coldkey( &coldkey_i, new_coldkey ).map(|w| weight = weight.saturating_add(w)) { + return Err("Failed to perform coldkey swap"); + } + } + } + + Ok(weight) + } + + + pub fn perform_swap_coldkey( old_coldkey: &T::AccountId, new_coldkey: &T::AccountId ) -> Result { // Swap coldkey references in storage maps // NOTE The order of these calls is important Self::swap_total_coldkey_stake(old_coldkey, new_coldkey, &mut weight); @@ -166,22 +307,29 @@ impl Pallet { // Transfer any remaining balance from old_coldkey to new_coldkey let remaining_balance = Self::get_coldkey_balance(old_coldkey); if remaining_balance > 0 { - Self::kill_coldkey_account(old_coldkey, remaining_balance)?; + if let Err(e) = Self::kill_coldkey_account(old_coldkey, remaining_balance) { + return Err(e); + } Self::add_balance_to_coldkey_account(new_coldkey, remaining_balance); } // Swap the coldkey. - let total_balance = Self::get_coldkey_balance( &old_coldkey ); - if !total_balance.is_zero() { + let total_balance:u64 = Self::get_coldkey_balance(&old_coldkey); + if total_balance > 0 { // Attempt to transfer the entire total balance to coldkeyB. - T::Currency::transfer( + if let Err(e) = T::Currency::transfer( &old_coldkey, &new_coldkey, total_balance, Preservation::Expendable, - ); + ) { + return Err(e); + } } + + Ok(*weight) } + /// Retrieves the network membership status for a given hotkey. /// /// # Arguments diff --git a/pallets/subtensor/tests/staking.rs b/pallets/subtensor/tests/staking.rs index ff15bf479..2d894b51d 100644 --- a/pallets/subtensor/tests/staking.rs +++ b/pallets/subtensor/tests/staking.rs @@ -3163,16 +3163,16 @@ fn test_do_unstake_all_and_transfer_to_new_coldkey_success() { &new_coldkey )); - // Check that ColdkeysToDrainTo is populated correctly + // Check that ColdkeySwapDestinations is populated correctly assert_eq!( - pallet_subtensor::ColdkeysToDrainTo::::get(current_coldkey), + pallet_subtensor::ColdkeySwapDestinations::::get(current_coldkey), vec![new_coldkey] ); // Check that drain block is set correctly let drain_block: u64 = 7200 * 7 + 1; assert_eq!( - pallet_subtensor::ColdkeysToDrainOnBlock::::get(drain_block), + pallet_subtensor::ColdkeysToArbitrateAtBlock::::get(drain_block), vec![current_coldkey] ); From 8851879fd8f3c6beb86e44c698197ebcf7a4b58e Mon Sep 17 00:00:00 2001 From: const Date: Sat, 6 Jul 2024 16:36:31 -0500 Subject: [PATCH 09/51] passing here --- pallets/subtensor/src/swap.rs | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/pallets/subtensor/src/swap.rs b/pallets/subtensor/src/swap.rs index 400144540..d92e48b4f 100644 --- a/pallets/subtensor/src/swap.rs +++ b/pallets/subtensor/src/swap.rs @@ -135,7 +135,7 @@ impl Pallet { ); // Actually do the swap. - weight = weight.saturating_add(Self::perform_swap_coldkey(old_coldkey, new_coldkey, &mut weight).map_err(|_| Error::::SwapError)?); + weight = weight.saturating_add(Self::perform_swap_coldkey( old_coldkey, new_coldkey ).map_err(|_| Error::::SwapError)?); Self::set_last_tx_block(new_coldkey, Self::get_current_block_as_u64() ); weight.saturating_accrue(T::DbWeight::get().writes(1)); @@ -215,7 +215,7 @@ impl Pallet { // Add the old coldkey to the next period keys to swap. // Sanity Check. - if !next_period_coldkeys_to_swap.contains( old_coldkey.clone() ) { + if !next_period_coldkeys_to_swap.contains( &old_coldkey.clone() ) { next_period_coldkeys_to_swap.push( old_coldkey.clone() ); } @@ -291,6 +291,10 @@ impl Pallet { pub fn perform_swap_coldkey( old_coldkey: &T::AccountId, new_coldkey: &T::AccountId ) -> Result { + + // Init the weight. + let mut weight = frame_support::weights::Weight::from_parts(0, 0); + // Swap coldkey references in storage maps // NOTE The order of these calls is important Self::swap_total_coldkey_stake(old_coldkey, new_coldkey, &mut weight); @@ -308,7 +312,7 @@ impl Pallet { let remaining_balance = Self::get_coldkey_balance(old_coldkey); if remaining_balance > 0 { if let Err(e) = Self::kill_coldkey_account(old_coldkey, remaining_balance) { - return Err(e); + return Err(e.into()); } Self::add_balance_to_coldkey_account(new_coldkey, remaining_balance); } @@ -323,11 +327,11 @@ impl Pallet { total_balance, Preservation::Expendable, ) { - return Err(e); + return Err(e.into()); } } - Ok(*weight) + Ok(weight) } /// Retrieves the network membership status for a given hotkey. From e7337af2b9115c882ea741f2fbe513aaf995830e Mon Sep 17 00:00:00 2001 From: const Date: Sat, 6 Jul 2024 17:51:45 -0500 Subject: [PATCH 10/51] fxi tests on arbitrated coldkey swap --- pallets/subtensor/src/lib.rs | 13 +-- pallets/subtensor/src/swap.rs | 97 +++++++---------------- pallets/subtensor/src/utils.rs | 7 ++ pallets/subtensor/tests/staking.rs | 123 ++++++++++++++--------------- pallets/subtensor/tests/swap.rs | 100 +++++++++++------------ 5 files changed, 153 insertions(+), 187 deletions(-) diff --git a/pallets/subtensor/src/lib.rs b/pallets/subtensor/src/lib.rs index f3da9adbc..3309b8fed 100644 --- a/pallets/subtensor/src/lib.rs +++ b/pallets/subtensor/src/lib.rs @@ -366,6 +366,8 @@ pub mod pallet { #[pallet::storage] // --- MAP ( cold ) --> Vec | Returns the vector of hotkeys controlled by this coldkey. pub type OwnedHotkeys = StorageMap<_, Blake2_128Concat, T::AccountId, Vec, ValueQuery>; + #[pallet::storage] // --- DMAP ( cold ) --> Vec | Maps coldkey to hotkeys that stake to it + pub type StakingHotkeys = StorageMap<_, Blake2_128Concat, T::AccountId, Vec, ValueQuery>; #[pallet::storage] // --- MAP ( hot ) --> take | Returns the hotkey delegation take. And signals that this key is open for delegation. pub type Delegates = StorageMap<_, Blake2_128Concat, T::AccountId, u16, ValueQuery, DefaultDefaultTake>; @@ -390,9 +392,6 @@ pub mod pallet { pub type ColdkeysToArbitrateAtBlock = StorageMap<_, Identity, u64, Vec, ValueQuery, EmptyAccounts>; - #[pallet::storage] // --- DMAP ( cold ) --> Vec | Maps coldkey to hotkeys that stake to it - pub type StakingHotkeys = StorageMap<_, Blake2_128Concat, T::AccountId, Vec, ValueQuery>; - /// -- ITEM (switches liquid alpha on) #[pallet::type_value] pub fn DefaultLiquidAlpha() -> bool { @@ -1321,7 +1320,7 @@ pub mod pallet { match block_step_result { Ok(_) => { // --- If the block step was successful, return the weight. - log::info!("Successfully ran block step."); + log::debug!("Successfully ran block step."); Weight::from_parts(110_634_229_000_u64, 0) .saturating_add(T::DbWeight::get().reads(8304_u64)) .saturating_add(T::DbWeight::get().writes(110_u64)) @@ -2058,11 +2057,13 @@ pub mod pallet { #[pallet::weight((Weight::from_parts(1_940_000_000, 0) .saturating_add(T::DbWeight::get().reads(272)) .saturating_add(T::DbWeight::get().writes(527)), DispatchClass::Operational, Pays::No))] - pub fn arbitrated_coldkey_swap( + pub fn schedule_arbitrated_coldkey_swap( origin: OriginFor, new_coldkey: T::AccountId, ) -> DispatchResult { - Self::do_arbitrated_coldkey_swap( origin, &new_coldkey ) + // Attain the calling coldkey from the origin. + let old_coldkey:T::AccountId = ensure_signed(origin)?; + Self::do_schedule_arbitrated_coldkey_swap( &old_coldkey, &new_coldkey ) } // ---- SUDO ONLY FUNCTIONS ------------------------------------------------------------ diff --git a/pallets/subtensor/src/swap.rs b/pallets/subtensor/src/swap.rs index d92e48b4f..a0fec9cfc 100644 --- a/pallets/subtensor/src/swap.rs +++ b/pallets/subtensor/src/swap.rs @@ -164,17 +164,14 @@ impl Pallet { /// // The coldkey is in arbitration state. pub fn coldkey_in_arbitration( coldkey: &T::AccountId ) -> bool { ColdkeySwapDestinations::::contains_key( coldkey ) } // Helper - pub fn do_arbitrated_coldkey_swap( - origin: T::RuntimeOrigin, + pub fn do_schedule_arbitrated_coldkey_swap( + old_coldkey: &T::AccountId, new_coldkey: &T::AccountId, ) -> DispatchResult { - // Attain the calling coldkey from the origin. - let old_coldkey:T::AccountId = ensure_signed(origin)?; - // Catch spurious swaps. ensure!( - old_coldkey != *new_coldkey, + *old_coldkey != *new_coldkey, Error::::SameColdkey ); @@ -241,6 +238,7 @@ impl Pallet { // Get the block number let current_block: u64 = Self::get_current_block_as_u64(); + log::debug!("Arbitrating coldkeys for block: {:?}", current_block); // Get the coldkeys to swap here and then remove them. let source_coldkeys: Vec = ColdkeysToArbitrateAtBlock::::get( current_block ); @@ -292,6 +290,7 @@ impl Pallet { pub fn perform_swap_coldkey( old_coldkey: &T::AccountId, new_coldkey: &T::AccountId ) -> Result { + log::info!("Performing swap for coldkey: {:?} to {:?}", old_coldkey, new_coldkey); // Init the weight. let mut weight = frame_support::weights::Weight::from_parts(0, 0); @@ -299,14 +298,12 @@ impl Pallet { // NOTE The order of these calls is important Self::swap_total_coldkey_stake(old_coldkey, new_coldkey, &mut weight); Self::swap_stake_for_coldkey(old_coldkey, new_coldkey, &mut weight); - Self::swap_owner_for_coldkey(old_coldkey, new_coldkey, &mut weight); Self::swap_total_hotkey_coldkey_stakes_this_interval_for_coldkey( old_coldkey, new_coldkey, &mut weight, ); Self::swap_subnet_owner_for_coldkey(old_coldkey, new_coldkey, &mut weight); - Self::swap_owned_for_coldkey(old_coldkey, new_coldkey, &mut weight); // Transfer any remaining balance from old_coldkey to new_coldkey let remaining_balance = Self::get_coldkey_balance(old_coldkey); @@ -453,9 +450,13 @@ impl Pallet { let mut staking_hotkeys = StakingHotkeys::::get(&coldkey); if !staking_hotkeys.contains(new_hotkey) { staking_hotkeys.push(new_hotkey.clone()); - StakingHotkeys::::insert(coldkey.clone(), staking_hotkeys); writes = writes.saturating_add(1u64); // One write for insert } + if let Some(pos) = staking_hotkeys.iter().position(|x| x == old_hotkey) { + staking_hotkeys.remove(pos); + writes = writes.saturating_add(1u64); // One write for remove + } + StakingHotkeys::::insert(coldkey.clone(), staking_hotkeys); } // Clear the prefix for the old hotkey after transferring all stakes @@ -700,45 +701,28 @@ impl Pallet { new_coldkey: &T::AccountId, weight: &mut Weight, ) { - // Find all hotkeys for this coldkey - let hotkeys = OwnedHotkeys::::get(old_coldkey); - weight.saturating_accrue(T::DbWeight::get().reads_writes(1, 0)); - for hotkey in hotkeys.iter() { - let stake = Stake::::get(&hotkey, old_coldkey); - Stake::::remove(&hotkey, old_coldkey); - Stake::::insert(&hotkey, new_coldkey, stake); - - // Update StakingHotkeys map - let staking_hotkeys = StakingHotkeys::::get(old_coldkey); - StakingHotkeys::::insert(new_coldkey.clone(), staking_hotkeys); + // Swap the owners. + let old_owned_hotkeys = OwnedHotkeys::::get(old_coldkey); + for owned_key in old_owned_hotkeys.iter() { + Owner::::insert(owned_key, new_coldkey); weight.saturating_accrue(T::DbWeight::get().reads_writes(2, 3)); } - } - - /// Swaps the owner of all hotkeys from the old coldkey to the new coldkey. - /// - /// # Arguments - /// - /// * `old_coldkey` - The AccountId of the old coldkey. - /// * `new_coldkey` - The AccountId of the new coldkey. - /// * `weight` - Mutable reference to the weight of the transaction. - /// - /// # Effects - /// - /// * Updates the owner of all hotkeys associated with the old coldkey to the new coldkey. - /// * Updates the transaction weight. - pub fn swap_owner_for_coldkey( - old_coldkey: &T::AccountId, - new_coldkey: &T::AccountId, - weight: &mut Weight, - ) { - let hotkeys = OwnedHotkeys::::get(old_coldkey); - weight.saturating_accrue(T::DbWeight::get().reads_writes(1, 0)); - for hotkey in hotkeys.iter() { - Owner::::insert(&hotkey, new_coldkey); - weight.saturating_accrue(T::DbWeight::get().reads_writes(0, 1)); + OwnedHotkeys::::remove( old_coldkey.clone() ); + OwnedHotkeys::::insert( new_coldkey.clone(), old_owned_hotkeys ); + + // Swap all the keys the coldkey is staking too. + let staking_hotkeys = StakingHotkeys::::get( old_coldkey ); + StakingHotkeys::::remove( old_coldkey.clone() ); + for hotkey in staking_hotkeys.iter() { + // Remove the previous stake and re-insert it. + let stake = Stake::::get(hotkey, old_coldkey); + Stake::::remove(hotkey, old_coldkey); + Stake::::insert(hotkey, new_coldkey, stake); + weight.saturating_accrue(T::DbWeight::get().reads_writes(2, 3)); } + // Add the new staking keys value. + StakingHotkeys::::insert( new_coldkey.clone(), staking_hotkeys.clone() ); } /// Swaps the total hotkey-coldkey stakes for the current interval from the old coldkey to the new coldkey. @@ -779,7 +763,7 @@ impl Pallet { /// /// * `bool` - True if the coldkey has any associated hotkeys, false otherwise. pub fn coldkey_has_associated_hotkeys(coldkey: &T::AccountId) -> bool { - Owner::::iter().any(|(_, owner)| owner == *coldkey) + StakingHotkeys::::get(coldkey).len() > 0 } /// Swaps the subnet owner from the old coldkey to the new coldkey for all networks where the old coldkey is the owner. @@ -809,27 +793,4 @@ impl Pallet { weight.saturating_accrue(T::DbWeight::get().reads(TotalNetworks::::get() as u64)); } - /// Swaps the owned hotkeys for the coldkey - /// - /// # Arguments - /// - /// * `old_coldkey` - The AccountId of the old coldkey. - /// * `new_coldkey` - The AccountId of the new coldkey. - /// * `weight` - Mutable reference to the weight of the transaction. - /// - /// # Effects - /// - /// * Updates the subnet owner to the new coldkey for all networks where the old coldkey was the owner. - /// * Updates the transaction weight. - pub fn swap_owned_for_coldkey( - old_coldkey: &T::AccountId, - new_coldkey: &T::AccountId, - weight: &mut Weight, - ) { - // Update OwnedHotkeys map with new coldkey - let hotkeys = OwnedHotkeys::::get(old_coldkey); - OwnedHotkeys::::remove(old_coldkey); - OwnedHotkeys::::insert(new_coldkey, hotkeys); - weight.saturating_accrue(T::DbWeight::get().reads_writes(0, 2)); - } } diff --git a/pallets/subtensor/src/utils.rs b/pallets/subtensor/src/utils.rs index faaaecebb..94f18dfbf 100644 --- a/pallets/subtensor/src/utils.rs +++ b/pallets/subtensor/src/utils.rs @@ -616,6 +616,13 @@ impl Pallet { Self::deposit_event(Event::SubnetOwnerCutSet(subnet_owner_cut)); } + pub fn get_owned_hotkeys(coldkey: &T::AccountId) -> Vec { + OwnedHotkeys::::get(coldkey) + } + pub fn get_all_staked_hotkeys(coldkey: &T::AccountId) -> Vec { + StakingHotkeys::::get(coldkey) + } + pub fn set_total_issuance(total_issuance: u64) { TotalIssuance::::put(total_issuance); } diff --git a/pallets/subtensor/tests/staking.rs b/pallets/subtensor/tests/staking.rs index 2d894b51d..eaef29474 100644 --- a/pallets/subtensor/tests/staking.rs +++ b/pallets/subtensor/tests/staking.rs @@ -3153,12 +3153,13 @@ fn setup_test_environment() -> (AccountId, AccountId, AccountId) { (current_coldkey, hotkey, new_coldkey) } +/// SKIP_WASM_BUILD=1 RUST_LOG=debug cargo test --test staking -- test_arbitrated_coldkey_swap_success --exact --nocapture #[test] -fn test_do_unstake_all_and_transfer_to_new_coldkey_success() { +fn test_arbitrated_coldkey_swap_success() { new_test_ext(1).execute_with(|| { let (current_coldkey, hotkey, new_coldkey) = setup_test_environment(); - assert_ok!(SubtensorModule::schedule_unstake_all_and_transfer_to_new_coldkey( + assert_ok!(SubtensorModule::do_schedule_arbitrated_coldkey_swap( ¤t_coldkey, &new_coldkey )); @@ -3175,38 +3176,36 @@ fn test_do_unstake_all_and_transfer_to_new_coldkey_success() { pallet_subtensor::ColdkeysToArbitrateAtBlock::::get(drain_block), vec![current_coldkey] ); + log::info!("Drain block set correctly: {:?}", drain_block); + log::info!("Drain block {:?}", pallet_subtensor::ColdkeysToArbitrateAtBlock::::get(drain_block)); // Make 7200 * 7 blocks pass run_to_block(drain_block); // Run unstaking - SubtensorModule::drain_all_pending_coldkeys(); + SubtensorModule::arbitrate_coldkeys_this_block().unwrap(); + log::info!("Arbitrated coldkeys for block: {:?}", SubtensorModule::get_current_block_as_u64()); - // Check that the stake has been removed - assert_eq!(SubtensorModule::get_total_stake_for_hotkey(&hotkey), 0); + // Check the hotkey stake. + assert_eq!(SubtensorModule::get_total_stake_for_hotkey(&hotkey), 500); + + // Get the owner of the hotkey now new key. + assert_eq!(SubtensorModule::get_owning_coldkey_for_hotkey(&hotkey), new_coldkey); // Check that the balance has been transferred to the new coldkey - assert_eq!(SubtensorModule::get_coldkey_balance(&new_coldkey), 1000); - - // Check that the appropriate event was emitted - System::assert_last_event( - Event::AllBalanceUnstakedAndTransferredToNewColdkey { - current_coldkey, - new_coldkey, - total_balance: 1000, - } - .into(), - ); + assert_eq!(SubtensorModule::get_coldkey_balance(&new_coldkey), 500); // The new key as the 500 + }); } +/// SKIP_WASM_BUILD=1 RUST_LOG=debug cargo test --test staking -- test_arbitrated_coldkey_swap_same_coldkey --exact --nocapture #[test] -fn test_do_unstake_all_and_transfer_to_new_coldkey_same_coldkey() { +fn test_arbitrated_coldkey_swap_same_coldkey() { new_test_ext(1).execute_with(|| { let (current_coldkey, _hotkey, _) = setup_test_environment(); assert_noop!( - SubtensorModule::schedule_unstake_all_and_transfer_to_new_coldkey( + SubtensorModule::do_schedule_arbitrated_coldkey_swap( ¤t_coldkey, ¤t_coldkey ), @@ -3215,8 +3214,9 @@ fn test_do_unstake_all_and_transfer_to_new_coldkey_same_coldkey() { }); } +/// SKIP_WASM_BUILD=1 RUST_LOG=debug cargo test --test staking -- test_arbitrated_coldkey_swap_no_balance --exact --nocapture #[test] -fn test_do_unstake_all_and_transfer_to_new_coldkey_no_balance() { +fn test_arbitrated_coldkey_swap_no_balance() { new_test_ext(1).execute_with(|| { // Create accounts manually let current_coldkey: AccountId = U256::from(1); @@ -3248,14 +3248,14 @@ fn test_do_unstake_all_and_transfer_to_new_coldkey_no_balance() { assert_eq!(Balances::total_balance(&new_coldkey), 0); // Try to unstake and transfer - let result = SubtensorModule::schedule_unstake_all_and_transfer_to_new_coldkey( + let result = SubtensorModule::do_schedule_arbitrated_coldkey_swap( ¤t_coldkey, &new_coldkey, ); // Print the result log::info!( - "Result of do_unstake_all_and_transfer_to_new_coldkey: {:?}", + "Result of arbitrated_coldkey_swap: {:?}", result ); @@ -3280,8 +3280,10 @@ fn test_do_unstake_all_and_transfer_to_new_coldkey_no_balance() { }); } +// To run this test, use the following command: +// SKIP_WASM_BUILD=1 RUST_LOG=debug cargo test --test staking -- test_arbitrated_coldkey_swap_with_no_stake --exact --nocapture #[test] -fn test_do_unstake_all_and_transfer_to_new_coldkey_with_no_stake() { +fn test_arbitrated_coldkey_swap_with_no_stake() { new_test_ext(1).execute_with(|| { // Create accounts manually let current_coldkey: AccountId = U256::from(1); @@ -3317,7 +3319,7 @@ fn test_do_unstake_all_and_transfer_to_new_coldkey_with_no_stake() { assert_eq!(Balances::total_balance(&new_coldkey), 0); // Perform unstake and transfer - assert_ok!(SubtensorModule::schedule_unstake_all_and_transfer_to_new_coldkey( + assert_ok!(SubtensorModule::do_schedule_arbitrated_coldkey_swap( ¤t_coldkey, &new_coldkey )); @@ -3327,7 +3329,7 @@ fn test_do_unstake_all_and_transfer_to_new_coldkey_with_no_stake() { run_to_block(drain_block); // Run unstaking - SubtensorModule::drain_all_pending_coldkeys(); + SubtensorModule::arbitrate_coldkeys_this_block().unwrap(); // Print final balances log::info!( @@ -3347,19 +3349,12 @@ fn test_do_unstake_all_and_transfer_to_new_coldkey_with_no_stake() { assert_eq!(Balances::total_balance(&new_coldkey), initial_balance); assert_eq!(Balances::total_balance(¤t_coldkey), 0); - // Check that the appropriate event was emitted - System::assert_last_event( - Event::AllBalanceUnstakedAndTransferredToNewColdkey { - current_coldkey, - new_coldkey, - total_balance: initial_balance, - } - .into(), - ); }); } + +// SKIP_WASM_BUILD=1 RUST_LOG=info cargo test --test staking -- test_arbitrated_coldkey_swap_with_multiple_stakes --exact --nocapture #[test] -fn test_do_unstake_all_and_transfer_to_new_coldkey_with_multiple_stakes() { +fn test_arbitrated_coldkey_swap_with_multiple_stakes() { new_test_ext(1).execute_with(|| { let (current_coldkey, hotkey, new_coldkey) = setup_test_environment(); @@ -3372,7 +3367,7 @@ fn test_do_unstake_all_and_transfer_to_new_coldkey_with_multiple_stakes() { 300 )); - assert_ok!(SubtensorModule::schedule_unstake_all_and_transfer_to_new_coldkey( + assert_ok!(SubtensorModule::do_schedule_arbitrated_coldkey_swap( ¤t_coldkey, &new_coldkey )); @@ -3382,28 +3377,26 @@ fn test_do_unstake_all_and_transfer_to_new_coldkey_with_multiple_stakes() { run_to_block(drain_block); // Run unstaking - SubtensorModule::drain_all_pending_coldkeys(); + SubtensorModule::arbitrate_coldkeys_this_block().unwrap(); // Check that all stake has been removed - assert_eq!(SubtensorModule::get_total_stake_for_hotkey(&hotkey), 0); + assert_eq!(SubtensorModule::get_total_stake_for_hotkey(&hotkey), 800); + + // Owner has changed + assert_eq!(SubtensorModule::get_owning_coldkey_for_hotkey(&hotkey), new_coldkey); // Check that the full balance has been transferred to the new coldkey - assert_eq!(SubtensorModule::get_coldkey_balance(&new_coldkey), 1000); - - // Check that the appropriate event was emitted - System::assert_last_event( - Event::AllBalanceUnstakedAndTransferredToNewColdkey { - current_coldkey, - new_coldkey, - total_balance: 1000, - } - .into(), - ); + assert_eq!(SubtensorModule::get_coldkey_balance(&new_coldkey), 200); + + // Check that the full balance has been transferred to the new coldkey + assert_eq!(SubtensorModule::get_coldkey_balance(¤t_coldkey), 0); + }); } +// SKIP_WASM_BUILD=1 RUST_LOG=info cargo test --test staking -- test_arbitrated_coldkey_swap_with_multiple_stakes_multiple --exact --nocapture #[test] -fn test_do_unstake_all_and_transfer_to_new_coldkey_with_multiple_stakes_multiple() { +fn test_arbitrated_coldkey_swap_with_multiple_stakes_multiple() { new_test_ext(1).execute_with(|| { // Register the neuron to a new network let netuid = 1; @@ -3426,7 +3419,7 @@ fn test_do_unstake_all_and_transfer_to_new_coldkey_with_multiple_stakes_multiple hotkey2, 300 )); - assert_ok!(SubtensorModule::schedule_unstake_all_and_transfer_to_new_coldkey( + assert_ok!(SubtensorModule::do_schedule_arbitrated_coldkey_swap( ¤t_coldkey, &new_coldkey )); @@ -3436,18 +3429,22 @@ fn test_do_unstake_all_and_transfer_to_new_coldkey_with_multiple_stakes_multiple run_to_block(drain_block); // Run unstaking - SubtensorModule::drain_all_pending_coldkeys(); - - assert_eq!(SubtensorModule::get_total_stake_for_hotkey(&hotkey0), 0); - assert_eq!(SubtensorModule::get_total_stake_for_hotkey(&hotkey2), 0); - assert_eq!(SubtensorModule::get_coldkey_balance(&new_coldkey), 1000); - System::assert_last_event( - Event::AllBalanceUnstakedAndTransferredToNewColdkey { - current_coldkey, - new_coldkey, - total_balance: 1000, - } - .into(), - ); + SubtensorModule::arbitrate_coldkeys_this_block().unwrap(); + + assert_eq!(SubtensorModule::get_total_stake_for_hotkey(&hotkey0), 500); + assert_eq!(SubtensorModule::get_total_stake_for_hotkey(&hotkey2), 300); + assert_eq!(SubtensorModule::get_coldkey_balance(&new_coldkey), 200); + assert_eq!(SubtensorModule::get_coldkey_balance(¤t_coldkey), 0); + // Owner has changed + assert_eq!(SubtensorModule::get_owning_coldkey_for_hotkey(&hotkey0), new_coldkey); + assert_eq!(SubtensorModule::get_owning_coldkey_for_hotkey(&hotkey2), new_coldkey); + // Get owned keys + assert_eq!(SubtensorModule::get_owned_hotkeys(&new_coldkey), vec![hotkey0, hotkey2]); + assert_eq!(SubtensorModule::get_owned_hotkeys(¤t_coldkey), vec![]); + // Get all staked keys. + assert_eq!(SubtensorModule::get_all_staked_hotkeys(&new_coldkey), vec![hotkey0, hotkey2]); + assert_eq!(SubtensorModule::get_all_staked_hotkeys(¤t_coldkey), vec![]); + // Check + }); } diff --git a/pallets/subtensor/tests/swap.rs b/pallets/subtensor/tests/swap.rs index d527c44e8..09dec6d8c 100644 --- a/pallets/subtensor/tests/swap.rs +++ b/pallets/subtensor/tests/swap.rs @@ -1169,6 +1169,7 @@ fn test_swap_total_coldkey_stake() { }); } +// SKIP_WASM_BUILD=1 RUST_LOG=info cargo test --test swap -- test_swap_stake_for_coldkey --exact --nocapture #[test] fn test_swap_stake_for_coldkey() { new_test_ext(1).execute_with(|| { @@ -1179,25 +1180,38 @@ fn test_swap_stake_for_coldkey() { let stake_amount1 = 1000u64; let stake_amount2 = 2000u64; let mut weight = Weight::zero(); + SubtensorModule::add_balance_to_coldkey_account(&old_coldkey, stake_amount1 + stake_amount2 + 123456); + add_network(1, 13, 0); + register_ok_neuron(1, hotkey1, old_coldkey, 0); + register_ok_neuron(1, hotkey2, old_coldkey, 0); + assert_ok!(SubtensorModule::add_stake( + <::RuntimeOrigin>::signed(old_coldkey), + hotkey1, + stake_amount1 + )); + assert_ok!(SubtensorModule::add_stake( + <::RuntimeOrigin>::signed(old_coldkey), + hotkey2, + stake_amount2 + )); - // Initialize Stake for old_coldkey - Stake::::insert(hotkey1, old_coldkey, stake_amount1); - Stake::::insert(hotkey2, old_coldkey, stake_amount2); - - // Initialize TotalHotkeyStake - TotalHotkeyStake::::insert(hotkey1, stake_amount1); - TotalHotkeyStake::::insert(hotkey2, stake_amount2); - - // Initialize TotalStake and TotalIssuance - TotalStake::::put(stake_amount1 + stake_amount2); - TotalIssuance::::put(stake_amount1 + stake_amount2); - - // Populate OwnedHotkeys map - OwnedHotkeys::::insert(old_coldkey, vec![hotkey1, hotkey2]); + // Get owned keys + assert_eq!(SubtensorModule::get_owned_hotkeys(&old_coldkey), vec![hotkey1, hotkey2]); + assert_eq!(SubtensorModule::get_owned_hotkeys(&new_coldkey), vec![]); + // Get all staked keys. + assert_eq!(SubtensorModule::get_all_staked_hotkeys(&old_coldkey), vec![hotkey1, hotkey2]); + assert_eq!(SubtensorModule::get_all_staked_hotkeys(&new_coldkey), vec![]); // Perform the swap SubtensorModule::swap_stake_for_coldkey(&old_coldkey, &new_coldkey, &mut weight); + // Get owned keys + assert_eq!(SubtensorModule::get_owned_hotkeys(&new_coldkey), vec![hotkey1, hotkey2]); + assert_eq!(SubtensorModule::get_owned_hotkeys(&old_coldkey), vec![]); + // Get all staked keys. + assert_eq!(SubtensorModule::get_all_staked_hotkeys(&new_coldkey), vec![hotkey1, hotkey2]); + assert_eq!(SubtensorModule::get_all_staked_hotkeys(&old_coldkey), vec![]); + // Verify the swap assert_eq!(Stake::::get(hotkey1, new_coldkey), stake_amount1); assert_eq!(Stake::::get(hotkey2, new_coldkey), stake_amount2); @@ -1207,45 +1221,9 @@ fn test_swap_stake_for_coldkey() { // Verify TotalHotkeyStake remains unchanged assert_eq!(TotalHotkeyStake::::get(hotkey1), stake_amount1); assert_eq!(TotalHotkeyStake::::get(hotkey2), stake_amount2); - - // Verify TotalStake and TotalIssuance remain unchanged - assert_eq!(TotalStake::::get(), stake_amount1 + stake_amount2); - assert_eq!(TotalIssuance::::get(), stake_amount1 + stake_amount2); - - // Verify weight update - let expected_weight = ::DbWeight::get().reads_writes(5, 6); - assert_eq!(weight, expected_weight); }); } -#[test] -fn test_swap_owner_for_coldkey() { - new_test_ext(1).execute_with(|| { - let old_coldkey = U256::from(1); - let new_coldkey = U256::from(2); - let hotkey1 = U256::from(3); - let hotkey2 = U256::from(4); - let mut weight = Weight::zero(); - - // Initialize Owner for old_coldkey - Owner::::insert(hotkey1, old_coldkey); - Owner::::insert(hotkey2, old_coldkey); - - // Initialize OwnedHotkeys map - OwnedHotkeys::::insert(old_coldkey, vec![hotkey1, hotkey2]); - - // Perform the swap - SubtensorModule::swap_owner_for_coldkey(&old_coldkey, &new_coldkey, &mut weight); - - // Verify the swap - assert_eq!(Owner::::get(hotkey1), new_coldkey); - assert_eq!(Owner::::get(hotkey2), new_coldkey); - - // Verify weight update - let expected_weight = ::DbWeight::get().reads_writes(1, 2); - assert_eq!(weight, expected_weight); - }); -} #[test] fn test_swap_total_hotkey_coldkey_stakes_this_interval_for_coldkey() { @@ -1380,3 +1358,25 @@ fn test_coldkey_has_associated_hotkeys() { )); }); } + +// #[test] +// fn test_coldkey_arbitrated_sw() { +// new_test_ext(1).execute_with(|| { +// let coldkey = U256::from(1); +// let hotkey = U256::from(2); +// let netuid = 1u16; + +// // Setup initial state +// add_network(netuid, 13, 0); +// register_ok_neuron(netuid, hotkey, coldkey, 0); + +// // Check if coldkey has associated hotkeys +// assert!(SubtensorModule::coldkey_has_associated_hotkeys(&coldkey)); + +// // Check for a coldkey without associated hotkeys +// let unassociated_coldkey = U256::from(3); +// assert!(!SubtensorModule::coldkey_has_associated_hotkeys( +// &unassociated_coldkey +// )); +// }); +// } From cf4c8d593f9b5c72ca2510b63ad35e97728229ba Mon Sep 17 00:00:00 2001 From: const Date: Sat, 6 Jul 2024 18:52:05 -0500 Subject: [PATCH 11/51] add more test --- pallets/subtensor/src/lib.rs | 9 ++- pallets/subtensor/src/swap.rs | 58 +++++---------- pallets/subtensor/tests/staking.rs | 115 +++++++++++++++++------------ 3 files changed, 92 insertions(+), 90 deletions(-) diff --git a/pallets/subtensor/src/lib.rs b/pallets/subtensor/src/lib.rs index 3309b8fed..f698585b0 100644 --- a/pallets/subtensor/src/lib.rs +++ b/pallets/subtensor/src/lib.rs @@ -383,11 +383,16 @@ pub mod pallet { DefaultAccountTake, >; - /// Default value for hotkeys. - #[pallet::type_value] + #[pallet::type_value] /// Default value for hotkeys. pub fn EmptyAccounts() -> Vec { vec![] } + #[pallet::type_value]/// Default stake interval. + pub fn DefaultArbitrationPeriod() -> u64 { 7200 * 4 } + #[pallet::storage] // ---- StorageItem Global Used Work. + pub type ArbitrationPeriod =StorageValue<_, u64, ValueQuery, DefaultArbitrationPeriod>; #[pallet::storage] // --- MAP ( cold ) --> Vec | Returns a list of keys to drain to, if there are two, we extend the period. pub type ColdkeySwapDestinations = StorageMap<_, Blake2_128Concat, T::AccountId, Vec, ValueQuery, EmptyAccounts>; + #[pallet::storage] // --- MAP ( cold ) --> u64 | Block when the coldkey will be arbitrated. + pub type ColdkeyArbitrationBlock = StorageMap<_, Blake2_128Concat, T::AccountId, u64, ValueQuery>; #[pallet::storage] // --- MAP ( u64 ) --> Vec | Coldkeys to drain on the specific block. pub type ColdkeysToArbitrateAtBlock = StorageMap<_, Identity, u64, Vec, ValueQuery, EmptyAccounts>; diff --git a/pallets/subtensor/src/swap.rs b/pallets/subtensor/src/swap.rs index a0fec9cfc..a3893dde3 100644 --- a/pallets/subtensor/src/swap.rs +++ b/pallets/subtensor/src/swap.rs @@ -163,7 +163,7 @@ impl Pallet { /// - There are already 2 keys in arbitration for this coldkey. /// // The coldkey is in arbitration state. - pub fn coldkey_in_arbitration( coldkey: &T::AccountId ) -> bool { ColdkeySwapDestinations::::contains_key( coldkey ) } // Helper + pub fn coldkey_in_arbitration( coldkey: &T::AccountId ) -> bool { ColdkeyArbitrationBlock::::get(coldkey) > Self::get_current_block_as_u64() } pub fn do_schedule_arbitrated_coldkey_swap( old_coldkey: &T::AccountId, new_coldkey: &T::AccountId, @@ -184,40 +184,29 @@ impl Pallet { Error::::DuplicateColdkey ); - // Add the wallet to the swap wallets. - let initial_destination_count = destination_coldkeys.len(); - // If the destinations keys are empty or have size 1 then we will add the new coldkey to the list. - if initial_destination_count == 0 as usize || initial_destination_count == 1 as usize { - // Extend the wallet to swap to. + if destination_coldkeys.len() == 0 as usize || destination_coldkeys.len() == 1 as usize { + // Add this wallet to exist in the destination list. destination_coldkeys.push( new_coldkey.clone() ); - - // Push the change to the storage. ColdkeySwapDestinations::::insert( old_coldkey.clone(), destination_coldkeys.clone() ); - } else { - - // If the destinations len > 1 we dont add the new coldkey. return Err(Error::::ColdkeyIsInArbitration.into()); } - // If this is the first time we have seen this key we will put the swap period to be in 7 days. - if initial_destination_count == 0 as usize { - - // Set the arbitration period to be 7 days from now. - let next_arbitration_period: u64 = Self::get_current_block_as_u64() + 7200 * 7; + // It is the first time we have seen this key. + if destination_coldkeys.len() == 1 as usize { - // First time seeing this key lets push the swap moment to 1 week in the future. - let mut next_period_coldkeys_to_swap: Vec = ColdkeysToArbitrateAtBlock::::get( next_arbitration_period ); + // Set the arbitration block for this coldkey. + let arbitration_block: u64 = Self::get_current_block_as_u64() + ArbitrationPeriod::::get(); + ColdkeyArbitrationBlock::::insert( old_coldkey.clone(), arbitration_block ); - // Add the old coldkey to the next period keys to swap. - // Sanity Check. - if !next_period_coldkeys_to_swap.contains( &old_coldkey.clone() ) { - next_period_coldkeys_to_swap.push( old_coldkey.clone() ); + // Update the list of coldkeys arbitrate on this block. + let mut key_to_arbitrate_on_this_block: Vec = ColdkeysToArbitrateAtBlock::::get( arbitration_block ); + if !key_to_arbitrate_on_this_block.contains( &old_coldkey.clone() ) { + key_to_arbitrate_on_this_block.push( old_coldkey.clone() ); } + ColdkeysToArbitrateAtBlock::::insert( arbitration_block, key_to_arbitrate_on_this_block ); - // Set the new coldkeys to swap here. - ColdkeysToArbitrateAtBlock::::insert( next_arbitration_period, next_period_coldkeys_to_swap ); } // Return true. @@ -253,25 +242,12 @@ impl Pallet { ColdkeySwapDestinations::::remove( &coldkey_i ); weight = weight.saturating_add(T::DbWeight::get().reads_writes(1, 1)); - // If the wallets to swap is > 1, we extend the period. + // If the wallets to swap is > 1 we do nothing. if destinations_coldkeys.len() > 1 { - // Next arbitrage period - let next_arbitrage_period: u64 = current_block + 7200 * 7; - - // Get the coldkeys to swap at the next arbitrage period. - let mut next_period_coldkeys_to_swap: Vec = ColdkeysToArbitrateAtBlock::::get( next_arbitrage_period ); - weight = weight.saturating_add(T::DbWeight::get().reads_writes(1, 0)); - - // Add this new coldkey to these coldkeys - // Sanity Check. - if !next_period_coldkeys_to_swap.contains(coldkey_i) { - next_period_coldkeys_to_swap.push(coldkey_i.clone()); - } - - // Set the new coldkeys to swap here. - ColdkeysToArbitrateAtBlock::::insert( next_arbitrage_period, next_period_coldkeys_to_swap ); - weight = weight.saturating_add(T::DbWeight::get().reads_writes(0, 1)); + // Update the arbitration period but we still have the same wallet to swap to. + let next_arbitrage_period: u64 = current_block + ArbitrationPeriod::::get(); + ColdkeyArbitrationBlock::::insert( coldkey_i.clone(), next_arbitrage_period ); } else if destinations_coldkeys.len() == 1 { // ONLY 1 wallet: Get the wallet to swap to. diff --git a/pallets/subtensor/tests/staking.rs b/pallets/subtensor/tests/staking.rs index eaef29474..9c87590ed 100644 --- a/pallets/subtensor/tests/staking.rs +++ b/pallets/subtensor/tests/staking.rs @@ -3394,57 +3394,78 @@ fn test_arbitrated_coldkey_swap_with_multiple_stakes() { }); } -// SKIP_WASM_BUILD=1 RUST_LOG=info cargo test --test staking -- test_arbitrated_coldkey_swap_with_multiple_stakes_multiple --exact --nocapture +// SKIP_WASM_BUILD=1 RUST_LOG=info cargo test --test staking -- test_arbitrated_coldkey_swap_multiple_arbitrations --exact --nocapture #[test] -fn test_arbitrated_coldkey_swap_with_multiple_stakes_multiple() { +fn test_arbitrated_coldkey_swap_multiple_arbitrations() { new_test_ext(1).execute_with(|| { - // Register the neuron to a new network - let netuid = 1; - let hotkey0 = U256::from(1); - let hotkey2 = U256::from(2); - let current_coldkey = U256::from(3); - let new_coldkey = U256::from(4); - add_network(netuid, 0, 0); - register_ok_neuron(1, hotkey0, current_coldkey, 0); - register_ok_neuron(1, hotkey2, current_coldkey, 0); - SubtensorModule::set_target_stakes_per_interval(10); - SubtensorModule::add_balance_to_coldkey_account(¤t_coldkey, 1000); - assert_ok!(SubtensorModule::add_stake( - RuntimeOrigin::signed(current_coldkey), - hotkey0, - 500 - )); - assert_ok!(SubtensorModule::add_stake( - RuntimeOrigin::signed(current_coldkey), - hotkey2, - 300 - )); - assert_ok!(SubtensorModule::do_schedule_arbitrated_coldkey_swap( - ¤t_coldkey, - &new_coldkey - )); - // Make 7200 * 7 blocks pass - let drain_block: u64 = 7200 * 7 + 1; - run_to_block(drain_block); + // Create coldkey with two choices. + let coldkey: AccountId = U256::from(1); + let new_coldkey1: AccountId = U256::from(2); + let new_coldkey2: AccountId = U256::from(3); + let hotkey: AccountId = U256::from(4); - // Run unstaking - SubtensorModule::arbitrate_coldkeys_this_block().unwrap(); + // Setup network state. + add_network(1, 0, 0); + SubtensorModule::add_balance_to_coldkey_account(&coldkey, 1000); + ArbitrationPeriod::::put(1); // One block arbitration period. + register_ok_neuron(1, hotkey, coldkey, 0); + + // Owner schedules a swap for themselves. + assert_ok!(SubtensorModule::do_schedule_arbitrated_coldkey_swap( &coldkey, &new_coldkey1 )); + + // Attacker schedules the second swap for themselves. + assert_ok!(SubtensorModule::do_schedule_arbitrated_coldkey_swap( &coldkey, &new_coldkey2 )); + + // Both keys are added in swap destinations. + assert_eq!( pallet_subtensor::ColdkeySwapDestinations::::get(coldkey), vec![new_coldkey1, new_coldkey2] ); + + // Check that we are arbitrating next block. + assert_eq!( pallet_subtensor::ColdkeysToArbitrateAtBlock::::get(SubtensorModule::get_current_block_as_u64() + 1), vec![coldkey] ); + + // Key is in arbitration. + assert!( SubtensorModule::coldkey_in_arbitration( &coldkey ) ); + + // Arbitrate next block. + assert_eq!( pallet_subtensor::ColdkeyArbitrationBlock::::get( &coldkey ), SubtensorModule::get_current_block_as_u64() + 1 ); + + // Arbitrate. + step_block(1); + + // Both keys are removed and a new period begins. + assert_eq!( pallet_subtensor::ColdkeySwapDestinations::::get(coldkey), vec![] ); + + // Arbitration has been pushed back but there are no keys to add to the list to arbitrate. + assert_eq!( pallet_subtensor::ColdkeysToArbitrateAtBlock::::get(SubtensorModule::get_current_block_as_u64() + 1), vec![] ); + + // Key is in arbitration. + assert!( SubtensorModule::coldkey_in_arbitration( &coldkey ) ); + + // Arbitrate next block. + assert_eq!( pallet_subtensor::ColdkeyArbitrationBlock::::get( &coldkey ), SubtensorModule::get_current_block_as_u64() + 1 ); + + // Arbitrate. + step_block(1); + + // Key is not in arbitration + assert!( !SubtensorModule::coldkey_in_arbitration( &coldkey ) ); + + // Owner schedules a swap for themselves leys go back into arbitration. + assert_ok!(SubtensorModule::do_schedule_arbitrated_coldkey_swap( &coldkey, &new_coldkey1 )); + + // Key goes back into arbitration. + assert!( SubtensorModule::coldkey_in_arbitration( &coldkey ) ); + + // Arbitrate next block. + assert_eq!( pallet_subtensor::ColdkeyArbitrationBlock::::get( &coldkey ), SubtensorModule::get_current_block_as_u64() + 1 ); + + // Arbitrate. + step_block(1); + + // New key gets amount the other keys are empty. + assert_eq!(SubtensorModule::get_coldkey_balance(&coldkey), 0); + assert_eq!(SubtensorModule::get_coldkey_balance(&new_coldkey1), 1000); + assert_eq!(SubtensorModule::get_coldkey_balance(&new_coldkey2), 0); - assert_eq!(SubtensorModule::get_total_stake_for_hotkey(&hotkey0), 500); - assert_eq!(SubtensorModule::get_total_stake_for_hotkey(&hotkey2), 300); - assert_eq!(SubtensorModule::get_coldkey_balance(&new_coldkey), 200); - assert_eq!(SubtensorModule::get_coldkey_balance(¤t_coldkey), 0); - // Owner has changed - assert_eq!(SubtensorModule::get_owning_coldkey_for_hotkey(&hotkey0), new_coldkey); - assert_eq!(SubtensorModule::get_owning_coldkey_for_hotkey(&hotkey2), new_coldkey); - // Get owned keys - assert_eq!(SubtensorModule::get_owned_hotkeys(&new_coldkey), vec![hotkey0, hotkey2]); - assert_eq!(SubtensorModule::get_owned_hotkeys(¤t_coldkey), vec![]); - // Get all staked keys. - assert_eq!(SubtensorModule::get_all_staked_hotkeys(&new_coldkey), vec![hotkey0, hotkey2]); - assert_eq!(SubtensorModule::get_all_staked_hotkeys(¤t_coldkey), vec![]); - // Check - }); } From c5b78f80f6ca84086e147dc77f537791762437ef Mon Sep 17 00:00:00 2001 From: Samuel Dare Date: Sun, 7 Jul 2024 05:25:01 +0400 Subject: [PATCH 12/51] chore: update comments, lints --- pallets/subtensor/src/lib.rs | 40 +++++-- pallets/subtensor/src/migration.rs | 9 +- pallets/subtensor/src/registration.rs | 5 +- pallets/subtensor/src/root.rs | 27 ++++- pallets/subtensor/src/staking.rs | 30 +++-- pallets/subtensor/src/swap.rs | 161 ++++++++++++++++---------- pallets/subtensor/tests/staking.rs | 100 +++++++++++----- pallets/subtensor/tests/swap.rs | 40 +++++-- 8 files changed, 276 insertions(+), 136 deletions(-) diff --git a/pallets/subtensor/src/lib.rs b/pallets/subtensor/src/lib.rs index f698585b0..3756fd98a 100644 --- a/pallets/subtensor/src/lib.rs +++ b/pallets/subtensor/src/lib.rs @@ -367,7 +367,8 @@ pub mod pallet { pub type OwnedHotkeys = StorageMap<_, Blake2_128Concat, T::AccountId, Vec, ValueQuery>; #[pallet::storage] // --- DMAP ( cold ) --> Vec | Maps coldkey to hotkeys that stake to it - pub type StakingHotkeys = StorageMap<_, Blake2_128Concat, T::AccountId, Vec, ValueQuery>; + pub type StakingHotkeys = + StorageMap<_, Blake2_128Concat, T::AccountId, Vec, ValueQuery>; #[pallet::storage] // --- MAP ( hot ) --> take | Returns the hotkey delegation take. And signals that this key is open for delegation. pub type Delegates = StorageMap<_, Blake2_128Concat, T::AccountId, u16, ValueQuery, DefaultDefaultTake>; @@ -383,19 +384,34 @@ pub mod pallet { DefaultAccountTake, >; - #[pallet::type_value] /// Default value for hotkeys. - pub fn EmptyAccounts() -> Vec { vec![] } - #[pallet::type_value]/// Default stake interval. - pub fn DefaultArbitrationPeriod() -> u64 { 7200 * 4 } + #[pallet::type_value] + /// Default value for hotkeys. + pub fn EmptyAccounts() -> Vec { + vec![] + } + #[pallet::type_value] + /// Default stake interval. + pub fn DefaultArbitrationPeriod() -> u64 { + 7200 * 4 + } #[pallet::storage] // ---- StorageItem Global Used Work. - pub type ArbitrationPeriod =StorageValue<_, u64, ValueQuery, DefaultArbitrationPeriod>; + pub type ArbitrationPeriod = + StorageValue<_, u64, ValueQuery, DefaultArbitrationPeriod>; #[pallet::storage] // --- MAP ( cold ) --> Vec | Returns a list of keys to drain to, if there are two, we extend the period. - pub type ColdkeySwapDestinations = StorageMap<_, Blake2_128Concat, T::AccountId, Vec, ValueQuery, EmptyAccounts>; + pub type ColdkeySwapDestinations = StorageMap< + _, + Blake2_128Concat, + T::AccountId, + Vec, + ValueQuery, + EmptyAccounts, + >; #[pallet::storage] // --- MAP ( cold ) --> u64 | Block when the coldkey will be arbitrated. - pub type ColdkeyArbitrationBlock = StorageMap<_, Blake2_128Concat, T::AccountId, u64, ValueQuery>; + pub type ColdkeyArbitrationBlock = + StorageMap<_, Blake2_128Concat, T::AccountId, u64, ValueQuery>; #[pallet::storage] // --- MAP ( u64 ) --> Vec | Coldkeys to drain on the specific block. - pub type ColdkeysToArbitrateAtBlock = StorageMap<_, Identity, u64, Vec, ValueQuery, EmptyAccounts>; - + pub type ColdkeysToArbitrateAtBlock = + StorageMap<_, Identity, u64, Vec, ValueQuery, EmptyAccounts>; /// -- ITEM (switches liquid alpha on) #[pallet::type_value] @@ -2067,8 +2083,8 @@ pub mod pallet { new_coldkey: T::AccountId, ) -> DispatchResult { // Attain the calling coldkey from the origin. - let old_coldkey:T::AccountId = ensure_signed(origin)?; - Self::do_schedule_arbitrated_coldkey_swap( &old_coldkey, &new_coldkey ) + let old_coldkey: T::AccountId = ensure_signed(origin)?; + Self::do_schedule_arbitrated_coldkey_swap(&old_coldkey, &new_coldkey) } // ---- SUDO ONLY FUNCTIONS ------------------------------------------------------------ diff --git a/pallets/subtensor/src/migration.rs b/pallets/subtensor/src/migration.rs index 400030450..35b3c4260 100644 --- a/pallets/subtensor/src/migration.rs +++ b/pallets/subtensor/src/migration.rs @@ -565,23 +565,23 @@ pub fn migrate_populate_staking_hotkeys() -> Weight { if stake > 0 { let mut hotkeys = StakingHotkeys::::get(&coldkey); storage_reads = storage_reads.saturating_add(1); // Read from StakingHotkeys storage - + // Add the hotkey if it's not already in the vector if !hotkeys.contains(&hotkey) { hotkeys.push(hotkey); keys_touched = keys_touched.saturating_add(1); - + // Update longest hotkey vector info if longest_hotkey_vector < hotkeys.len() { longest_hotkey_vector = hotkeys.len(); longest_coldkey = Some(coldkey.clone()); } - + // Update the StakingHotkeys storage StakingHotkeys::::insert(&coldkey, hotkeys); storage_writes = storage_writes.saturating_add(1); // Write to StakingHotkeys storage } - + // Accrue weight for reads and writes weight = weight.saturating_add(T::DbWeight::get().reads_writes(2, 1)); } @@ -603,4 +603,3 @@ pub fn migrate_populate_staking_hotkeys() -> Weight { Weight::zero() } } - diff --git a/pallets/subtensor/src/registration.rs b/pallets/subtensor/src/registration.rs index 3a4af23f2..6b73f2fc3 100644 --- a/pallets/subtensor/src/registration.rs +++ b/pallets/subtensor/src/registration.rs @@ -41,7 +41,10 @@ impl Pallet { ) -> DispatchResult { // --- 1. Check that the caller has signed the transaction. (the coldkey of the pairing) let coldkey = ensure_signed(origin)?; - ensure!(!Self::coldkey_in_arbitration(&coldkey), Error::::ColdkeyIsInArbitration); + ensure!( + !Self::coldkey_in_arbitration(&coldkey), + Error::::ColdkeyIsInArbitration + ); log::info!( "do_registration( coldkey:{:?} netuid:{:?} hotkey:{:?} )", coldkey, diff --git a/pallets/subtensor/src/root.rs b/pallets/subtensor/src/root.rs index 2db11e302..f39ae97e0 100644 --- a/pallets/subtensor/src/root.rs +++ b/pallets/subtensor/src/root.rs @@ -483,7 +483,10 @@ impl Pallet { // --- 1. Ensure that the call originates from a signed source and retrieve the caller's account ID (coldkey). let coldkey = ensure_signed(origin)?; - ensure!(!Self::coldkey_in_arbitration(&coldkey), Error::::ColdkeyIsInArbitration); + ensure!( + !Self::coldkey_in_arbitration(&coldkey), + Error::::ColdkeyIsInArbitration + ); log::info!( "do_root_register( coldkey: {:?}, hotkey: {:?} )", coldkey, @@ -624,7 +627,10 @@ impl Pallet { ) -> dispatch::DispatchResult { // Check the caller's signature. This is the coldkey of a registered account. let coldkey = ensure_signed(origin)?; - ensure!(!Self::coldkey_in_arbitration(&coldkey), Error::::ColdkeyIsInArbitration); + ensure!( + !Self::coldkey_in_arbitration(&coldkey), + Error::::ColdkeyIsInArbitration + ); log::info!( "do_set_root_weights( origin:{:?} netuid:{:?}, uids:{:?}, values:{:?})", coldkey, @@ -746,7 +752,10 @@ impl Pallet { ) -> DispatchResultWithPostInfo { // --- 1. Ensure that the caller has signed with their coldkey. let coldkey = ensure_signed(origin.clone())?; - ensure!(!Self::coldkey_in_arbitration(&coldkey), Error::::ColdkeyIsInArbitration); + ensure!( + !Self::coldkey_in_arbitration(&coldkey), + Error::::ColdkeyIsInArbitration + ); // --- 2. Ensure that the calling coldkey owns the associated hotkey. ensure!( @@ -800,8 +809,11 @@ impl Pallet { pub fn user_add_network(origin: T::RuntimeOrigin) -> dispatch::DispatchResult { // --- 0. Ensure the caller is a signed user. let coldkey = ensure_signed(origin)?; - ensure!(!Self::coldkey_in_arbitration(&coldkey), Error::::ColdkeyIsInArbitration); - + ensure!( + !Self::coldkey_in_arbitration(&coldkey), + Error::::ColdkeyIsInArbitration + ); + // --- 1. Rate limit for network registrations. let current_block = Self::get_current_block_as_u64(); let last_lock_block = Self::get_network_last_lock_block(); @@ -889,7 +901,10 @@ impl Pallet { pub fn user_remove_network(origin: T::RuntimeOrigin, netuid: u16) -> dispatch::DispatchResult { // --- 1. Ensure the function caller is a signed user. let coldkey = ensure_signed(origin)?; - ensure!(!Self::coldkey_in_arbitration(&coldkey), Error::::ColdkeyIsInArbitration); + ensure!( + !Self::coldkey_in_arbitration(&coldkey), + Error::::ColdkeyIsInArbitration + ); // --- 2. Ensure this subnet exists. ensure!( diff --git a/pallets/subtensor/src/staking.rs b/pallets/subtensor/src/staking.rs index b2ffe3da0..4ddca3fa1 100644 --- a/pallets/subtensor/src/staking.rs +++ b/pallets/subtensor/src/staking.rs @@ -7,7 +7,7 @@ use frame_support::{ Fortitude, Precision, Preservation, }, Imbalance, - } + }, }; impl Pallet { @@ -44,7 +44,10 @@ impl Pallet { ) -> dispatch::DispatchResult { // --- 1. We check the coldkey signuture. let coldkey = ensure_signed(origin)?; - ensure!(!Self::coldkey_in_arbitration(&coldkey), Error::::ColdkeyIsInArbitration); + ensure!( + !Self::coldkey_in_arbitration(&coldkey), + Error::::ColdkeyIsInArbitration + ); log::info!( "do_become_delegate( origin:{:?} hotkey:{:?}, take:{:?} )", coldkey, @@ -134,7 +137,10 @@ impl Pallet { hotkey, take ); - ensure!(!Self::coldkey_in_arbitration(&coldkey), Error::::ColdkeyIsInArbitration); + ensure!( + !Self::coldkey_in_arbitration(&coldkey), + Error::::ColdkeyIsInArbitration + ); // --- 2. Ensure we are delegating a known key. // Ensure that the coldkey is the owner. @@ -207,7 +213,10 @@ impl Pallet { hotkey, take ); - ensure!(!Self::coldkey_in_arbitration(&coldkey), Error::::ColdkeyIsInArbitration); + ensure!( + !Self::coldkey_in_arbitration(&coldkey), + Error::::ColdkeyIsInArbitration + ); // --- 2. Ensure we are delegating a known key. // Ensure that the coldkey is the owner. @@ -293,7 +302,10 @@ impl Pallet { hotkey, stake_to_be_added ); - ensure!(!Self::coldkey_in_arbitration(&coldkey), Error::::ColdkeyIsInArbitration); + ensure!( + !Self::coldkey_in_arbitration(&coldkey), + Error::::ColdkeyIsInArbitration + ); // Ensure the callers coldkey has enough stake to perform the transaction. ensure!( @@ -406,7 +418,10 @@ impl Pallet { hotkey, stake_to_be_removed ); - ensure!(!Self::coldkey_in_arbitration(&coldkey), Error::::ColdkeyIsInArbitration); + ensure!( + !Self::coldkey_in_arbitration(&coldkey), + Error::::ColdkeyIsInArbitration + ); // Ensure that the hotkey account exists this is only possible through registration. ensure!( @@ -854,8 +869,6 @@ impl Pallet { } } - - // pub fn x( coldkey_a: &T::AccountId, coldkey_b: &T::AccountId ) -> Weight { // let mut weight = frame_support::weights::Weight::from_parts(0, 0); @@ -905,5 +918,4 @@ impl Pallet { // weight // } - } diff --git a/pallets/subtensor/src/swap.rs b/pallets/subtensor/src/swap.rs index a3893dde3..a9a47db4d 100644 --- a/pallets/subtensor/src/swap.rs +++ b/pallets/subtensor/src/swap.rs @@ -1,8 +1,8 @@ use super::*; -use sp_core::Get; use frame_support::traits::fungible::Mutate; use frame_support::traits::tokens::Preservation; use frame_support::{storage::IterableStorageDoubleMap, weights::Weight}; +use sp_core::Get; impl Pallet { /// Swaps the hotkey of a coldkey account. @@ -30,7 +30,10 @@ impl Pallet { new_hotkey: &T::AccountId, ) -> DispatchResultWithPostInfo { let coldkey = ensure_signed(origin)?; - ensure!(!Self::coldkey_in_arbitration(&coldkey), Error::::ColdkeyIsInArbitration); + ensure!( + !Self::coldkey_in_arbitration(&coldkey), + Error::::ColdkeyIsInArbitration + ); let mut weight = T::DbWeight::get().reads(2); @@ -119,7 +122,10 @@ impl Pallet { new_coldkey: &T::AccountId, ) -> DispatchResultWithPostInfo { let coldkey_performing_swap = ensure_signed(origin)?; - ensure!(!Self::coldkey_in_arbitration(&coldkey_performing_swap), Error::::ColdkeyIsInArbitration); + ensure!( + !Self::coldkey_in_arbitration(&coldkey_performing_swap), + Error::::ColdkeyIsInArbitration + ); let mut weight: Weight = T::DbWeight::get().reads(2); @@ -135,9 +141,12 @@ impl Pallet { ); // Actually do the swap. - weight = weight.saturating_add(Self::perform_swap_coldkey( old_coldkey, new_coldkey ).map_err(|_| Error::::SwapError)?); + weight = weight.saturating_add( + Self::perform_swap_coldkey(old_coldkey, new_coldkey) + .map_err(|_| Error::::SwapError)?, + ); - Self::set_last_tx_block(new_coldkey, Self::get_current_block_as_u64() ); + Self::set_last_tx_block(new_coldkey, Self::get_current_block_as_u64()); weight.saturating_accrue(T::DbWeight::get().writes(1)); Self::deposit_event(Event::ColdkeySwapped { @@ -148,65 +157,91 @@ impl Pallet { Ok(Some(weight).into()) } - /// Swaps the coldkey associated with a set of hotkeys from an old coldkey to a new coldkey over a delayed - /// to faciliate arbitration. + /// Checks if a coldkey is currently in arbitration. /// /// # Arguments /// - /// * `origin` - The origin of the call, which must be signed by the old coldkey. + /// * `coldkey` - The account ID of the coldkey to check. + /// + /// # Returns + /// + /// * `bool` - True if the coldkey is in arbitration, false otherwise. + /// + /// # Notes + /// + /// This function compares the arbitration block number of the coldkey with the current block number. + pub fn coldkey_in_arbitration(coldkey: &T::AccountId) -> bool { + ColdkeyArbitrationBlock::::get(coldkey) > Self::get_current_block_as_u64() + } + + /// Schedules a coldkey swap to a new coldkey with arbitration. + /// + /// # Arguments + /// + /// * `old_coldkey` - The account ID of the old coldkey. /// * `new_coldkey` - The account ID of the new coldkey. /// + /// # Returns + /// + /// * `DispatchResult` - The result of the dispatch. + /// /// # Errors /// /// This function will return an error if: - /// - The caller is the same key as the new key or if the new key is already in the arbitration keys. - /// - There are already 2 keys in arbitration for this coldkey. - /// - // The coldkey is in arbitration state. - pub fn coldkey_in_arbitration( coldkey: &T::AccountId ) -> bool { ColdkeyArbitrationBlock::::get(coldkey) > Self::get_current_block_as_u64() } + /// - The old coldkey is the same as the new coldkey. + /// - The new coldkey is already in the list of destination coldkeys. + /// - There are already 2 destination coldkeys for the old coldkey. + /// + /// # Notes + /// + /// This function ensures that the new coldkey is not already in the list of destination coldkeys. + /// If the list of destination coldkeys is empty or has only one entry, the new coldkey is added to the list. + /// If the list of destination coldkeys has two entries, the function returns an error. + /// If the new coldkey is added to the list for the first time, the arbitration block is set for the old coldkey. + /// The list of coldkeys to arbitrate at the arbitration block is updated. pub fn do_schedule_arbitrated_coldkey_swap( old_coldkey: &T::AccountId, new_coldkey: &T::AccountId, ) -> DispatchResult { - // Catch spurious swaps. - ensure!( - *old_coldkey != *new_coldkey, - Error::::SameColdkey - ); + ensure!(*old_coldkey != *new_coldkey, Error::::SameColdkey); // Get current destination coldkeys. - let mut destination_coldkeys: Vec = ColdkeySwapDestinations::::get( old_coldkey.clone() ); + let mut destination_coldkeys: Vec = + ColdkeySwapDestinations::::get(old_coldkey.clone()); // Check if the new coldkey is already in the swap wallets list ensure!( - !destination_coldkeys.contains( new_coldkey ), + !destination_coldkeys.contains(new_coldkey), Error::::DuplicateColdkey ); - + // If the destinations keys are empty or have size 1 then we will add the new coldkey to the list. - if destination_coldkeys.len() == 0 as usize || destination_coldkeys.len() == 1 as usize { + if destination_coldkeys.is_empty() || destination_coldkeys.len() == 1_usize { // Add this wallet to exist in the destination list. - destination_coldkeys.push( new_coldkey.clone() ); - ColdkeySwapDestinations::::insert( old_coldkey.clone(), destination_coldkeys.clone() ); + destination_coldkeys.push(new_coldkey.clone()); + ColdkeySwapDestinations::::insert(old_coldkey.clone(), destination_coldkeys.clone()); } else { return Err(Error::::ColdkeyIsInArbitration.into()); } // It is the first time we have seen this key. - if destination_coldkeys.len() == 1 as usize { - + if destination_coldkeys.len() == 1_usize { // Set the arbitration block for this coldkey. - let arbitration_block: u64 = Self::get_current_block_as_u64() + ArbitrationPeriod::::get(); - ColdkeyArbitrationBlock::::insert( old_coldkey.clone(), arbitration_block ); + let arbitration_block: u64 = + Self::get_current_block_as_u64().saturating_add(ArbitrationPeriod::::get()); + ColdkeyArbitrationBlock::::insert(old_coldkey.clone(), arbitration_block); // Update the list of coldkeys arbitrate on this block. - let mut key_to_arbitrate_on_this_block: Vec = ColdkeysToArbitrateAtBlock::::get( arbitration_block ); - if !key_to_arbitrate_on_this_block.contains( &old_coldkey.clone() ) { - key_to_arbitrate_on_this_block.push( old_coldkey.clone() ); + let mut key_to_arbitrate_on_this_block: Vec = + ColdkeysToArbitrateAtBlock::::get(arbitration_block); + if !key_to_arbitrate_on_this_block.contains(&old_coldkey.clone()) { + key_to_arbitrate_on_this_block.push(old_coldkey.clone()); } - ColdkeysToArbitrateAtBlock::::insert( arbitration_block, key_to_arbitrate_on_this_block ); - + ColdkeysToArbitrateAtBlock::::insert( + arbitration_block, + key_to_arbitrate_on_this_block, + ); } // Return true. @@ -230,31 +265,32 @@ impl Pallet { log::debug!("Arbitrating coldkeys for block: {:?}", current_block); // Get the coldkeys to swap here and then remove them. - let source_coldkeys: Vec = ColdkeysToArbitrateAtBlock::::get( current_block ); - ColdkeysToArbitrateAtBlock::::remove( current_block ); + let source_coldkeys: Vec = + ColdkeysToArbitrateAtBlock::::get(current_block); + ColdkeysToArbitrateAtBlock::::remove(current_block); weight = weight.saturating_add(T::DbWeight::get().reads_writes(1, 1)); // Iterate over all keys in swap and call perform_swap_coldkey for each for coldkey_i in source_coldkeys.iter() { - // Get the wallets to swap to for this coldkey. - let destinations_coldkeys: Vec = ColdkeySwapDestinations::::get( coldkey_i ); - ColdkeySwapDestinations::::remove( &coldkey_i ); + let destinations_coldkeys: Vec = + ColdkeySwapDestinations::::get(coldkey_i); + ColdkeySwapDestinations::::remove(&coldkey_i); weight = weight.saturating_add(T::DbWeight::get().reads_writes(1, 1)); // If the wallets to swap is > 1 we do nothing. if destinations_coldkeys.len() > 1 { - // Update the arbitration period but we still have the same wallet to swap to. - let next_arbitrage_period: u64 = current_block + ArbitrationPeriod::::get(); - ColdkeyArbitrationBlock::::insert( coldkey_i.clone(), next_arbitrage_period ); - - } else if destinations_coldkeys.len() == 1 { + let next_arbitrage_period: u64 = + current_block.saturating_add(ArbitrationPeriod::::get()); + ColdkeyArbitrationBlock::::insert(coldkey_i.clone(), next_arbitrage_period); + } else if let Some(new_coldkey) = destinations_coldkeys.first() { // ONLY 1 wallet: Get the wallet to swap to. - let new_coldkey = &destinations_coldkeys[0]; - // Perform the swap. - if let Err(_) = Self::perform_swap_coldkey( &coldkey_i, new_coldkey ).map(|w| weight = weight.saturating_add(w)) { + if Self::perform_swap_coldkey(coldkey_i, new_coldkey) + .map(|w| weight = weight.saturating_add(w)) + .is_err() + { return Err("Failed to perform coldkey swap"); } } @@ -263,10 +299,15 @@ impl Pallet { Ok(weight) } - - pub fn perform_swap_coldkey( old_coldkey: &T::AccountId, new_coldkey: &T::AccountId ) -> Result { - - log::info!("Performing swap for coldkey: {:?} to {:?}", old_coldkey, new_coldkey); + pub fn perform_swap_coldkey( + old_coldkey: &T::AccountId, + new_coldkey: &T::AccountId, + ) -> Result { + log::info!( + "Performing swap for coldkey: {:?} to {:?}", + old_coldkey, + new_coldkey + ); // Init the weight. let mut weight = frame_support::weights::Weight::from_parts(0, 0); @@ -291,12 +332,12 @@ impl Pallet { } // Swap the coldkey. - let total_balance:u64 = Self::get_coldkey_balance(&old_coldkey); + let total_balance: u64 = Self::get_coldkey_balance(old_coldkey); if total_balance > 0 { // Attempt to transfer the entire total balance to coldkeyB. if let Err(e) = T::Currency::transfer( - &old_coldkey, - &new_coldkey, + old_coldkey, + new_coldkey, total_balance, Preservation::Expendable, ) { @@ -677,19 +718,18 @@ impl Pallet { new_coldkey: &T::AccountId, weight: &mut Weight, ) { - // Swap the owners. let old_owned_hotkeys = OwnedHotkeys::::get(old_coldkey); for owned_key in old_owned_hotkeys.iter() { Owner::::insert(owned_key, new_coldkey); weight.saturating_accrue(T::DbWeight::get().reads_writes(2, 3)); } - OwnedHotkeys::::remove( old_coldkey.clone() ); - OwnedHotkeys::::insert( new_coldkey.clone(), old_owned_hotkeys ); + OwnedHotkeys::::remove(old_coldkey.clone()); + OwnedHotkeys::::insert(new_coldkey.clone(), old_owned_hotkeys); // Swap all the keys the coldkey is staking too. - let staking_hotkeys = StakingHotkeys::::get( old_coldkey ); - StakingHotkeys::::remove( old_coldkey.clone() ); + let staking_hotkeys = StakingHotkeys::::get(old_coldkey); + StakingHotkeys::::remove(old_coldkey.clone()); for hotkey in staking_hotkeys.iter() { // Remove the previous stake and re-insert it. let stake = Stake::::get(hotkey, old_coldkey); @@ -698,7 +738,7 @@ impl Pallet { weight.saturating_accrue(T::DbWeight::get().reads_writes(2, 3)); } // Add the new staking keys value. - StakingHotkeys::::insert( new_coldkey.clone(), staking_hotkeys.clone() ); + StakingHotkeys::::insert(new_coldkey.clone(), staking_hotkeys.clone()); } /// Swaps the total hotkey-coldkey stakes for the current interval from the old coldkey to the new coldkey. @@ -739,7 +779,7 @@ impl Pallet { /// /// * `bool` - True if the coldkey has any associated hotkeys, false otherwise. pub fn coldkey_has_associated_hotkeys(coldkey: &T::AccountId) -> bool { - StakingHotkeys::::get(coldkey).len() > 0 + !StakingHotkeys::::get(coldkey).is_empty() } /// Swaps the subnet owner from the old coldkey to the new coldkey for all networks where the old coldkey is the owner. @@ -768,5 +808,4 @@ impl Pallet { } weight.saturating_accrue(T::DbWeight::get().reads(TotalNetworks::::get() as u64)); } - } diff --git a/pallets/subtensor/tests/staking.rs b/pallets/subtensor/tests/staking.rs index 9c87590ed..f45cde2fc 100644 --- a/pallets/subtensor/tests/staking.rs +++ b/pallets/subtensor/tests/staking.rs @@ -3177,24 +3177,32 @@ fn test_arbitrated_coldkey_swap_success() { vec![current_coldkey] ); log::info!("Drain block set correctly: {:?}", drain_block); - log::info!("Drain block {:?}", pallet_subtensor::ColdkeysToArbitrateAtBlock::::get(drain_block)); + log::info!( + "Drain block {:?}", + pallet_subtensor::ColdkeysToArbitrateAtBlock::::get(drain_block) + ); // Make 7200 * 7 blocks pass run_to_block(drain_block); // Run unstaking SubtensorModule::arbitrate_coldkeys_this_block().unwrap(); - log::info!("Arbitrated coldkeys for block: {:?}", SubtensorModule::get_current_block_as_u64()); + log::info!( + "Arbitrated coldkeys for block: {:?}", + SubtensorModule::get_current_block_as_u64() + ); // Check the hotkey stake. assert_eq!(SubtensorModule::get_total_stake_for_hotkey(&hotkey), 500); // Get the owner of the hotkey now new key. - assert_eq!(SubtensorModule::get_owning_coldkey_for_hotkey(&hotkey), new_coldkey); + assert_eq!( + SubtensorModule::get_owning_coldkey_for_hotkey(&hotkey), + new_coldkey + ); // Check that the balance has been transferred to the new coldkey assert_eq!(SubtensorModule::get_coldkey_balance(&new_coldkey), 500); // The new key as the 500 - }); } @@ -3248,16 +3256,11 @@ fn test_arbitrated_coldkey_swap_no_balance() { assert_eq!(Balances::total_balance(&new_coldkey), 0); // Try to unstake and transfer - let result = SubtensorModule::do_schedule_arbitrated_coldkey_swap( - ¤t_coldkey, - &new_coldkey, - ); + let result = + SubtensorModule::do_schedule_arbitrated_coldkey_swap(¤t_coldkey, &new_coldkey); // Print the result - log::info!( - "Result of arbitrated_coldkey_swap: {:?}", - result - ); + log::info!("Result of arbitrated_coldkey_swap: {:?}", result); // Print final balances log::info!( @@ -3348,7 +3351,6 @@ fn test_arbitrated_coldkey_swap_with_no_stake() { // Check that the balance has been transferred to the new coldkey assert_eq!(Balances::total_balance(&new_coldkey), initial_balance); assert_eq!(Balances::total_balance(¤t_coldkey), 0); - }); } @@ -3383,14 +3385,16 @@ fn test_arbitrated_coldkey_swap_with_multiple_stakes() { assert_eq!(SubtensorModule::get_total_stake_for_hotkey(&hotkey), 800); // Owner has changed - assert_eq!(SubtensorModule::get_owning_coldkey_for_hotkey(&hotkey), new_coldkey); + assert_eq!( + SubtensorModule::get_owning_coldkey_for_hotkey(&hotkey), + new_coldkey + ); // Check that the full balance has been transferred to the new coldkey assert_eq!(SubtensorModule::get_coldkey_balance(&new_coldkey), 200); // Check that the full balance has been transferred to the new coldkey assert_eq!(SubtensorModule::get_coldkey_balance(¤t_coldkey), 0); - }); } @@ -3398,7 +3402,6 @@ fn test_arbitrated_coldkey_swap_with_multiple_stakes() { #[test] fn test_arbitrated_coldkey_swap_multiple_arbitrations() { new_test_ext(1).execute_with(|| { - // Create coldkey with two choices. let coldkey: AccountId = U256::from(1); let new_coldkey1: AccountId = U256::from(2); @@ -3412,52 +3415,86 @@ fn test_arbitrated_coldkey_swap_multiple_arbitrations() { register_ok_neuron(1, hotkey, coldkey, 0); // Owner schedules a swap for themselves. - assert_ok!(SubtensorModule::do_schedule_arbitrated_coldkey_swap( &coldkey, &new_coldkey1 )); + assert_ok!(SubtensorModule::do_schedule_arbitrated_coldkey_swap( + &coldkey, + &new_coldkey1 + )); // Attacker schedules the second swap for themselves. - assert_ok!(SubtensorModule::do_schedule_arbitrated_coldkey_swap( &coldkey, &new_coldkey2 )); + assert_ok!(SubtensorModule::do_schedule_arbitrated_coldkey_swap( + &coldkey, + &new_coldkey2 + )); // Both keys are added in swap destinations. - assert_eq!( pallet_subtensor::ColdkeySwapDestinations::::get(coldkey), vec![new_coldkey1, new_coldkey2] ); + assert_eq!( + pallet_subtensor::ColdkeySwapDestinations::::get(coldkey), + vec![new_coldkey1, new_coldkey2] + ); // Check that we are arbitrating next block. - assert_eq!( pallet_subtensor::ColdkeysToArbitrateAtBlock::::get(SubtensorModule::get_current_block_as_u64() + 1), vec![coldkey] ); + assert_eq!( + pallet_subtensor::ColdkeysToArbitrateAtBlock::::get( + SubtensorModule::get_current_block_as_u64() + 1 + ), + vec![coldkey] + ); // Key is in arbitration. - assert!( SubtensorModule::coldkey_in_arbitration( &coldkey ) ); + assert!(SubtensorModule::coldkey_in_arbitration(&coldkey)); // Arbitrate next block. - assert_eq!( pallet_subtensor::ColdkeyArbitrationBlock::::get( &coldkey ), SubtensorModule::get_current_block_as_u64() + 1 ); + assert_eq!( + pallet_subtensor::ColdkeyArbitrationBlock::::get(coldkey), + SubtensorModule::get_current_block_as_u64() + 1 + ); // Arbitrate. step_block(1); // Both keys are removed and a new period begins. - assert_eq!( pallet_subtensor::ColdkeySwapDestinations::::get(coldkey), vec![] ); + assert_eq!( + pallet_subtensor::ColdkeySwapDestinations::::get(coldkey), + vec![] + ); // Arbitration has been pushed back but there are no keys to add to the list to arbitrate. - assert_eq!( pallet_subtensor::ColdkeysToArbitrateAtBlock::::get(SubtensorModule::get_current_block_as_u64() + 1), vec![] ); + assert_eq!( + pallet_subtensor::ColdkeysToArbitrateAtBlock::::get( + SubtensorModule::get_current_block_as_u64() + 1 + ), + vec![] + ); // Key is in arbitration. - assert!( SubtensorModule::coldkey_in_arbitration( &coldkey ) ); + assert!(SubtensorModule::coldkey_in_arbitration(&coldkey)); // Arbitrate next block. - assert_eq!( pallet_subtensor::ColdkeyArbitrationBlock::::get( &coldkey ), SubtensorModule::get_current_block_as_u64() + 1 ); + assert_eq!( + pallet_subtensor::ColdkeyArbitrationBlock::::get(coldkey), + SubtensorModule::get_current_block_as_u64() + 1 + ); // Arbitrate. - step_block(1); + step_block(1); // Key is not in arbitration - assert!( !SubtensorModule::coldkey_in_arbitration( &coldkey ) ); + assert!(!SubtensorModule::coldkey_in_arbitration(&coldkey)); // Owner schedules a swap for themselves leys go back into arbitration. - assert_ok!(SubtensorModule::do_schedule_arbitrated_coldkey_swap( &coldkey, &new_coldkey1 )); + assert_ok!(SubtensorModule::do_schedule_arbitrated_coldkey_swap( + &coldkey, + &new_coldkey1 + )); // Key goes back into arbitration. - assert!( SubtensorModule::coldkey_in_arbitration( &coldkey ) ); + assert!(SubtensorModule::coldkey_in_arbitration(&coldkey)); // Arbitrate next block. - assert_eq!( pallet_subtensor::ColdkeyArbitrationBlock::::get( &coldkey ), SubtensorModule::get_current_block_as_u64() + 1 ); + assert_eq!( + pallet_subtensor::ColdkeyArbitrationBlock::::get(coldkey), + SubtensorModule::get_current_block_as_u64() + 1 + ); // Arbitrate. step_block(1); @@ -3466,6 +3503,5 @@ fn test_arbitrated_coldkey_swap_multiple_arbitrations() { assert_eq!(SubtensorModule::get_coldkey_balance(&coldkey), 0); assert_eq!(SubtensorModule::get_coldkey_balance(&new_coldkey1), 1000); assert_eq!(SubtensorModule::get_coldkey_balance(&new_coldkey2), 0); - }); } diff --git a/pallets/subtensor/tests/swap.rs b/pallets/subtensor/tests/swap.rs index 09dec6d8c..48541abb4 100644 --- a/pallets/subtensor/tests/swap.rs +++ b/pallets/subtensor/tests/swap.rs @@ -1180,7 +1180,10 @@ fn test_swap_stake_for_coldkey() { let stake_amount1 = 1000u64; let stake_amount2 = 2000u64; let mut weight = Weight::zero(); - SubtensorModule::add_balance_to_coldkey_account(&old_coldkey, stake_amount1 + stake_amount2 + 123456); + SubtensorModule::add_balance_to_coldkey_account( + &old_coldkey, + stake_amount1 + stake_amount2 + 123456, + ); add_network(1, 13, 0); register_ok_neuron(1, hotkey1, old_coldkey, 0); register_ok_neuron(1, hotkey2, old_coldkey, 0); @@ -1195,22 +1198,40 @@ fn test_swap_stake_for_coldkey() { stake_amount2 )); - // Get owned keys - assert_eq!(SubtensorModule::get_owned_hotkeys(&old_coldkey), vec![hotkey1, hotkey2]); + // Get owned keys + assert_eq!( + SubtensorModule::get_owned_hotkeys(&old_coldkey), + vec![hotkey1, hotkey2] + ); assert_eq!(SubtensorModule::get_owned_hotkeys(&new_coldkey), vec![]); // Get all staked keys. - assert_eq!(SubtensorModule::get_all_staked_hotkeys(&old_coldkey), vec![hotkey1, hotkey2]); - assert_eq!(SubtensorModule::get_all_staked_hotkeys(&new_coldkey), vec![]); + assert_eq!( + SubtensorModule::get_all_staked_hotkeys(&old_coldkey), + vec![hotkey1, hotkey2] + ); + assert_eq!( + SubtensorModule::get_all_staked_hotkeys(&new_coldkey), + vec![] + ); // Perform the swap SubtensorModule::swap_stake_for_coldkey(&old_coldkey, &new_coldkey, &mut weight); - // Get owned keys - assert_eq!(SubtensorModule::get_owned_hotkeys(&new_coldkey), vec![hotkey1, hotkey2]); + // Get owned keys + assert_eq!( + SubtensorModule::get_owned_hotkeys(&new_coldkey), + vec![hotkey1, hotkey2] + ); assert_eq!(SubtensorModule::get_owned_hotkeys(&old_coldkey), vec![]); // Get all staked keys. - assert_eq!(SubtensorModule::get_all_staked_hotkeys(&new_coldkey), vec![hotkey1, hotkey2]); - assert_eq!(SubtensorModule::get_all_staked_hotkeys(&old_coldkey), vec![]); + assert_eq!( + SubtensorModule::get_all_staked_hotkeys(&new_coldkey), + vec![hotkey1, hotkey2] + ); + assert_eq!( + SubtensorModule::get_all_staked_hotkeys(&old_coldkey), + vec![] + ); // Verify the swap assert_eq!(Stake::::get(hotkey1, new_coldkey), stake_amount1); @@ -1224,7 +1245,6 @@ fn test_swap_stake_for_coldkey() { }); } - #[test] fn test_swap_total_hotkey_coldkey_stakes_this_interval_for_coldkey() { new_test_ext(1).execute_with(|| { From dad3925990ecddacf29d433d3b53345bfbb91b21 Mon Sep 17 00:00:00 2001 From: Samuel Dare Date: Sun, 7 Jul 2024 15:13:30 +0400 Subject: [PATCH 13/51] fix: tests , feat: benchmarks --- pallets/subtensor/src/benchmarks.rs | 35 ++++++++ pallets/subtensor/tests/staking.rs | 135 +++++++++++++++++++++++++--- scripts/benchmark.sh | 48 +++++----- 3 files changed, 182 insertions(+), 36 deletions(-) diff --git a/pallets/subtensor/src/benchmarks.rs b/pallets/subtensor/src/benchmarks.rs index 403ba413e..9d754ed65 100644 --- a/pallets/subtensor/src/benchmarks.rs +++ b/pallets/subtensor/src/benchmarks.rs @@ -428,4 +428,39 @@ reveal_weights { let _ = Subtensor::::commit_weights(::RuntimeOrigin::from(RawOrigin::Signed(hotkey.clone())), netuid, commit_hash); }: reveal_weights(RawOrigin::Signed(hotkey.clone()), netuid, uids, weight_values, salt, version_key) + + schedule_arbitrated_coldkey_swap { + let seed: u32 = 1; + let old_coldkey: T::AccountId = account("OldColdkey", 0, seed); + let new_coldkey: T::AccountId = account("NewColdkey", 0, seed + 1); + let hotkey: T::AccountId = account("Hotkey", 0, seed); + + let netuid = 1u16; + let tempo = 1u16; + + // Initialize the network + Subtensor::::init_new_network(netuid, tempo); + Subtensor::::set_network_registration_allowed(netuid, true); + // Subtensor::::set_network_pow_registration_allowed(netuid, true); + + // Add balance to the old coldkey account + let amount_to_be_staked = 1000000u32.into(); + Subtensor::::add_balance_to_coldkey_account(&old_coldkey.clone(), amount_to_be_staked); + + // // Register the hotkey with the old coldkey + // let block_number: u64 = Subtensor::::get_current_block_as_u64(); + // let (nonce, work): (u64, Vec) = Subtensor::::create_work_for_block_number( + // netuid, + // block_number, + // 3, + // &hotkey, + // ); + // Burned register the hotkey with the old coldkey + assert_ok!(Subtensor::::burned_register( + RawOrigin::Signed(old_coldkey.clone()).into(), + netuid, + hotkey.clone() + )); + + }: schedule_arbitrated_coldkey_swap(RawOrigin::Signed(old_coldkey.clone()), new_coldkey) } diff --git a/pallets/subtensor/tests/staking.rs b/pallets/subtensor/tests/staking.rs index f45cde2fc..83c318dbb 100644 --- a/pallets/subtensor/tests/staking.rs +++ b/pallets/subtensor/tests/staking.rs @@ -3171,7 +3171,13 @@ fn test_arbitrated_coldkey_swap_success() { ); // Check that drain block is set correctly - let drain_block: u64 = 7200 * 7 + 1; + let drain_block: u64 = 7200 * 4 + 1; + + log::info!( + "ColdkeysToArbitrateAtBlock before scheduling: {:?}", + pallet_subtensor::ColdkeysToArbitrateAtBlock::::get(drain_block) + ); + assert_eq!( pallet_subtensor::ColdkeysToArbitrateAtBlock::::get(drain_block), vec![current_coldkey] @@ -3182,7 +3188,7 @@ fn test_arbitrated_coldkey_swap_success() { pallet_subtensor::ColdkeysToArbitrateAtBlock::::get(drain_block) ); - // Make 7200 * 7 blocks pass + // Make 7200 * 4 blocks pass run_to_block(drain_block); // Run unstaking @@ -3327,13 +3333,10 @@ fn test_arbitrated_coldkey_swap_with_no_stake() { &new_coldkey )); - // Make 7200 * 7 blocks pass - let drain_block: u64 = 7200 * 7 + 1; + // Make 7200 * 4 blocks pass + let drain_block: u64 = 7200 * 4 + 1; run_to_block(drain_block); - // Run unstaking - SubtensorModule::arbitrate_coldkeys_this_block().unwrap(); - // Print final balances log::info!( "Final current_coldkey balance: {:?}", @@ -3374,13 +3377,10 @@ fn test_arbitrated_coldkey_swap_with_multiple_stakes() { &new_coldkey )); - // Make 7200 * 7 blocks pass - let drain_block: u64 = 7200 * 7 + 1; + // Make 7200 * 4 blocks pass + let drain_block: u64 = 7200 * 4 + 1; run_to_block(drain_block); - // Run unstaking - SubtensorModule::arbitrate_coldkeys_this_block().unwrap(); - // Check that all stake has been removed assert_eq!(SubtensorModule::get_total_stake_for_hotkey(&hotkey), 800); @@ -3505,3 +3505,114 @@ fn test_arbitrated_coldkey_swap_multiple_arbitrations() { assert_eq!(SubtensorModule::get_coldkey_balance(&new_coldkey2), 0); }); } + +// TODO: Verify that we never want more than 2 destinations for a coldkey +#[test] +fn test_arbitrated_coldkey_swap_existing_destination() { + new_test_ext(1).execute_with(|| { + let (current_coldkey, _hotkey, new_coldkey) = setup_test_environment(); + let another_coldkey = U256::from(4); + + // Schedule a swap to new_coldkey + assert_ok!(SubtensorModule::do_schedule_arbitrated_coldkey_swap( + ¤t_coldkey, + &new_coldkey + )); + + // Attempt to schedule a swap to the same new_coldkey again + assert_noop!( + SubtensorModule::do_schedule_arbitrated_coldkey_swap(¤t_coldkey, &new_coldkey), + Error::::DuplicateColdkey + ); + + // Schedule a swap to another_coldkey + assert_ok!(SubtensorModule::do_schedule_arbitrated_coldkey_swap( + ¤t_coldkey, + &another_coldkey + )); + + // Attempt to schedule a third swap + let third_coldkey = U256::from(5); + assert_noop!( + SubtensorModule::do_schedule_arbitrated_coldkey_swap(¤t_coldkey, &third_coldkey), + Error::::ColdkeyIsInArbitration + ); + }); +} + +#[test] +fn test_arbitration_period_extension() { + new_test_ext(1).execute_with(|| { + let (current_coldkey, _hotkey, new_coldkey) = setup_test_environment(); + let another_coldkey = U256::from(4); + + // Schedule a swap to new_coldkey + assert_ok!(SubtensorModule::do_schedule_arbitrated_coldkey_swap( + ¤t_coldkey, + &new_coldkey + )); + + // Schedule a swap to another_coldkey + assert_ok!(SubtensorModule::do_schedule_arbitrated_coldkey_swap( + ¤t_coldkey, + &another_coldkey + )); + + // Check that the arbitration period is extended + let arbitration_block = + SubtensorModule::get_current_block_as_u64() + ArbitrationPeriod::::get(); + assert_eq!( + pallet_subtensor::ColdkeyArbitrationBlock::::get(current_coldkey), + arbitration_block + ); + }); +} + +#[test] +fn test_concurrent_arbitrated_coldkey_swaps() { + new_test_ext(1).execute_with(|| { + // Manually create accounts + let coldkey1: AccountId = U256::from(1); + let hotkey1: AccountId = U256::from(2); + let new_coldkey1: AccountId = U256::from(3); + + let coldkey2: AccountId = U256::from(4); + let hotkey2: AccountId = U256::from(5); + let new_coldkey2: AccountId = U256::from(6); + + // Add networks + let netuid1: u16 = 1; + let netuid2: u16 = 2; + add_network(netuid1, 13, 0); + add_network(netuid2, 13, 0); + + // Register neurons in different networks + register_ok_neuron(netuid1, hotkey1, coldkey1, 0); + register_ok_neuron(netuid2, hotkey2, coldkey2, 0); + + // Add balance to coldkeys + SubtensorModule::add_balance_to_coldkey_account(&coldkey1, 1000); + SubtensorModule::add_balance_to_coldkey_account(&coldkey2, 1000); + + // Schedule swaps for both coldkeys + assert_ok!(SubtensorModule::do_schedule_arbitrated_coldkey_swap( + &coldkey1, + &new_coldkey1 + )); + assert_ok!(SubtensorModule::do_schedule_arbitrated_coldkey_swap( + &coldkey2, + &new_coldkey2 + )); + + // Make 7200 * 4 blocks pass + let drain_block: u64 = 7200 * 4 + 1; + run_to_block(drain_block); + + // Run arbitration + SubtensorModule::arbitrate_coldkeys_this_block().unwrap(); + + // Check that the balances have been transferred correctly + assert_eq!(SubtensorModule::get_coldkey_balance(&new_coldkey1), 1000); + assert_eq!(SubtensorModule::get_coldkey_balance(&new_coldkey2), 1000); + }); +} diff --git a/scripts/benchmark.sh b/scripts/benchmark.sh index 52bdaf2c5..4cba2ae7a 100755 --- a/scripts/benchmark.sh +++ b/scripts/benchmark.sh @@ -7,31 +7,31 @@ OUTPUT_FILE='benchmarking.txt' # Getting arguments from user while [[ $# -gt 0 ]]; do - case $1 in - -p | --bin-path) - BIN_PATH="$2" - shift - shift - ;; - -* | --*) - echo "Unknown option $1" - exit 1 - ;; - *) - POSITIONAL_ARGS+=("$1") - shift - ;; - esac + case $1 in + -p | --bin-path) + BIN_PATH="$2" + shift + shift + ;; + -* | --*) + echo "Unknown option $1" + exit 1 + ;; + *) + POSITIONAL_ARGS+=("$1") + shift + ;; + esac done # Ensure binary exists before node-subtensor executions if [ ! -f $BIN_PATH ]; then - if [[ "$DEFAULT_BIN_PATH" == "$BIN_PATH" ]]; then - cargo build --profile production --features runtime-benchmarks - else - echo "Binary '$BIN_PATH' does not exist. You can use -p or --bin-path to specify a different location." - exit 1 - fi + if [[ "$DEFAULT_BIN_PATH" == "$BIN_PATH" ]]; then + cargo build --profile production --features runtime-benchmarks + else + echo "Binary '$BIN_PATH' does not exist. You can use -p or --bin-path to specify a different location." + exit 1 + fi fi # Build Temporary Spec @@ -39,8 +39,8 @@ $BIN_PATH build-spec --disable-default-bootnode --raw --chain local >$TMP_SPEC # Run benchmark $BIN_PATH benchmark pallet \ - --chain=$TMP_SPEC \ - --pallet pallet-subtensor --extrinsic 'benchmark_dissolve_network' \ - --output $OUTPUT_FILE +--chain=$TMP_SPEC \ +--pallet pallet-subtensor --extrinsic 'schedule_arbitrated_coldkey_swap' \ +--output $OUTPUT_FILE rm $TMP_SPEC From 9a3f72578b11c1fcd9972d425b04cbbafb0dab4a Mon Sep 17 00:00:00 2001 From: Samuel Dare Date: Sun, 7 Jul 2024 16:04:48 +0400 Subject: [PATCH 14/51] fix: benchmarks --- pallets/subtensor/src/benchmarks.rs | 14 ++------------ pallets/subtensor/src/lib.rs | 6 +++--- 2 files changed, 5 insertions(+), 15 deletions(-) diff --git a/pallets/subtensor/src/benchmarks.rs b/pallets/subtensor/src/benchmarks.rs index 9d754ed65..f70b8bdad 100644 --- a/pallets/subtensor/src/benchmarks.rs +++ b/pallets/subtensor/src/benchmarks.rs @@ -441,20 +441,10 @@ reveal_weights { // Initialize the network Subtensor::::init_new_network(netuid, tempo); Subtensor::::set_network_registration_allowed(netuid, true); - // Subtensor::::set_network_pow_registration_allowed(netuid, true); // Add balance to the old coldkey account - let amount_to_be_staked = 1000000u32.into(); - Subtensor::::add_balance_to_coldkey_account(&old_coldkey.clone(), amount_to_be_staked); - - // // Register the hotkey with the old coldkey - // let block_number: u64 = Subtensor::::get_current_block_as_u64(); - // let (nonce, work): (u64, Vec) = Subtensor::::create_work_for_block_number( - // netuid, - // block_number, - // 3, - // &hotkey, - // ); + let amount_to_be_staked: u64 = 1000000u32.into(); + Subtensor::::add_balance_to_coldkey_account(&old_coldkey.clone(), amount_to_be_staked+1000000000); // Burned register the hotkey with the old coldkey assert_ok!(Subtensor::::burned_register( RawOrigin::Signed(old_coldkey.clone()).into(), diff --git a/pallets/subtensor/src/lib.rs b/pallets/subtensor/src/lib.rs index 3756fd98a..2eb5d4df2 100644 --- a/pallets/subtensor/src/lib.rs +++ b/pallets/subtensor/src/lib.rs @@ -2075,9 +2075,9 @@ pub mod pallet { /// /// Weight is calculated based on the number of database reads and writes. #[pallet::call_index(72)] - #[pallet::weight((Weight::from_parts(1_940_000_000, 0) - .saturating_add(T::DbWeight::get().reads(272)) - .saturating_add(T::DbWeight::get().writes(527)), DispatchClass::Operational, Pays::No))] + #[pallet::weight((Weight::from_parts(21_000_000, 0) + .saturating_add(T::DbWeight::get().reads(3)) + .saturating_add(T::DbWeight::get().writes(3)), DispatchClass::Operational, Pays::No))] pub fn schedule_arbitrated_coldkey_swap( origin: OriginFor, new_coldkey: T::AccountId, From f32c54614ecd8cd38216d2b8f98c22af1c618356 Mon Sep 17 00:00:00 2001 From: Samuel Dare Date: Sun, 7 Jul 2024 19:13:55 +0400 Subject: [PATCH 15/51] chore: whitelist schedule_arbitrated_coldkey_swap and set_weights, set_root_weights --- runtime/src/lib.rs | 21 ++------------------- 1 file changed, 2 insertions(+), 19 deletions(-) diff --git a/runtime/src/lib.rs b/runtime/src/lib.rs index 71b506247..01595a75f 100644 --- a/runtime/src/lib.rs +++ b/runtime/src/lib.rs @@ -312,26 +312,9 @@ impl Contains for SafeModeWhitelistedCalls { | RuntimeCall::SafeMode(_) | RuntimeCall::Timestamp(_) | RuntimeCall::SubtensorModule( - pallet_subtensor::Call::add_stake { .. } - | pallet_subtensor::Call::become_delegate { .. } - | pallet_subtensor::Call::burned_register { .. } - | pallet_subtensor::Call::commit_weights { .. } - | pallet_subtensor::Call::decrease_take { .. } - | pallet_subtensor::Call::faucet { .. } - | pallet_subtensor::Call::increase_take { .. } - | pallet_subtensor::Call::register { .. } - | pallet_subtensor::Call::register_network { .. } - | pallet_subtensor::Call::remove_stake { .. } - | pallet_subtensor::Call::reveal_weights { .. } - | pallet_subtensor::Call::root_register { .. } - | pallet_subtensor::Call::serve_axon { .. } - | pallet_subtensor::Call::serve_prometheus { .. } - | pallet_subtensor::Call::set_root_weights { .. } + pallet_subtensor::Call::do_schedule_arbitrated_coldkey_swap { .. } | pallet_subtensor::Call::set_weights { .. } - | pallet_subtensor::Call::sudo { .. } - | pallet_subtensor::Call::sudo_unchecked_weight { .. } - | pallet_subtensor::Call::swap_hotkey { .. } - | pallet_subtensor::Call::vote { .. } + | pallet_subtensor::Call::set_root_weights { .. } ) ) } From 68df4e18fe0664b645aa022cf59a149b54fb9e98 Mon Sep 17 00:00:00 2001 From: Samuel Dare Date: Sun, 7 Jul 2024 22:06:56 +0400 Subject: [PATCH 16/51] feat: signed extension arbitration , swap cold key min amount --- pallets/subtensor/src/benchmarks.rs | 4 +- pallets/subtensor/src/lib.rs | 14 +- pallets/subtensor/src/swap.rs | 48 ++++-- pallets/subtensor/tests/staking.rs | 231 ++++++++++++++++++++++++---- runtime/src/lib.rs | 2 +- scripts/benchmark.sh | 2 +- 6 files changed, 254 insertions(+), 47 deletions(-) diff --git a/pallets/subtensor/src/benchmarks.rs b/pallets/subtensor/src/benchmarks.rs index f70b8bdad..cb5bbff8e 100644 --- a/pallets/subtensor/src/benchmarks.rs +++ b/pallets/subtensor/src/benchmarks.rs @@ -429,7 +429,7 @@ reveal_weights { }: reveal_weights(RawOrigin::Signed(hotkey.clone()), netuid, uids, weight_values, salt, version_key) - schedule_arbitrated_coldkey_swap { + schedule_coldkey_swap { let seed: u32 = 1; let old_coldkey: T::AccountId = account("OldColdkey", 0, seed); let new_coldkey: T::AccountId = account("NewColdkey", 0, seed + 1); @@ -452,5 +452,5 @@ reveal_weights { hotkey.clone() )); - }: schedule_arbitrated_coldkey_swap(RawOrigin::Signed(old_coldkey.clone()), new_coldkey) + }: schedule_coldkey_swap(RawOrigin::Signed(old_coldkey.clone()), new_coldkey) } diff --git a/pallets/subtensor/src/lib.rs b/pallets/subtensor/src/lib.rs index 2eb5d4df2..3059ee84d 100644 --- a/pallets/subtensor/src/lib.rs +++ b/pallets/subtensor/src/lib.rs @@ -410,7 +410,7 @@ pub mod pallet { pub type ColdkeyArbitrationBlock = StorageMap<_, Blake2_128Concat, T::AccountId, u64, ValueQuery>; #[pallet::storage] // --- MAP ( u64 ) --> Vec | Coldkeys to drain on the specific block. - pub type ColdkeysToArbitrateAtBlock = + pub type ColdkeysToSwapAtBlock = StorageMap<_, Identity, u64, Vec, ValueQuery, EmptyAccounts>; /// -- ITEM (switches liquid alpha on) @@ -1332,7 +1332,7 @@ pub mod pallet { // - The number of the block we are initializing. fn on_initialize(_block_number: BlockNumberFor) -> Weight { // Unstake all and transfer pending coldkeys - let swap_weight = match Self::arbitrate_coldkeys_this_block() { + let swap_weight = match Self::swap_coldkeys_this_block() { Ok(weight) => weight, Err(_) => Weight::from_parts(0, 0), }; @@ -2078,13 +2078,13 @@ pub mod pallet { #[pallet::weight((Weight::from_parts(21_000_000, 0) .saturating_add(T::DbWeight::get().reads(3)) .saturating_add(T::DbWeight::get().writes(3)), DispatchClass::Operational, Pays::No))] - pub fn schedule_arbitrated_coldkey_swap( + pub fn schedule_coldkey_swap( origin: OriginFor, new_coldkey: T::AccountId, ) -> DispatchResult { // Attain the calling coldkey from the origin. let old_coldkey: T::AccountId = ensure_signed(origin)?; - Self::do_schedule_arbitrated_coldkey_swap(&old_coldkey, &new_coldkey) + Self::do_schedule_coldkey_swap(&old_coldkey, &new_coldkey) } // ---- SUDO ONLY FUNCTIONS ------------------------------------------------------------ @@ -2340,6 +2340,12 @@ where _info: &DispatchInfoOf, _len: usize, ) -> TransactionValidity { + if Pallet::::coldkey_in_arbitration(who) { + return Err(TransactionValidityError::Invalid( + InvalidTransaction::Call.into(), + )); + } + match call.is_sub_type() { Some(Call::commit_weights { netuid, .. }) => { if Self::check_weights_min_stake(who) { diff --git a/pallets/subtensor/src/swap.rs b/pallets/subtensor/src/swap.rs index a9a47db4d..48a3ca1cc 100644 --- a/pallets/subtensor/src/swap.rs +++ b/pallets/subtensor/src/swap.rs @@ -174,6 +174,31 @@ impl Pallet { ColdkeyArbitrationBlock::::get(coldkey) > Self::get_current_block_as_u64() } + /// Returns the remaining arbitration period for a given coldkey. + /// + /// # Arguments + /// + /// * `coldkey` - The account ID of the coldkey to check. + /// + /// # Returns + /// + /// * `u64` - The remaining arbitration period in blocks. + /// + /// + /// # Notes + /// + /// This function calculates the remaining arbitration period by subtracting the current block number + /// from the arbitration block number of the coldkey. + pub fn get_remaining_arbitration_period(coldkey: &T::AccountId) -> u64 { + let current_block: u64 = Self::get_current_block_as_u64(); + let arbitration_block: u64 = ColdkeyArbitrationBlock::::get(coldkey); + if arbitration_block > current_block { + arbitration_block.saturating_sub(current_block) + } else { + 0 + } + } + /// Schedules a coldkey swap to a new coldkey with arbitration. /// /// # Arguments @@ -199,10 +224,15 @@ impl Pallet { /// If the list of destination coldkeys has two entries, the function returns an error. /// If the new coldkey is added to the list for the first time, the arbitration block is set for the old coldkey. /// The list of coldkeys to arbitrate at the arbitration block is updated. - pub fn do_schedule_arbitrated_coldkey_swap( + // TOOD: + // Check minimum amount of TAO + // Add POW functionality / Move Destination Coldkeys to a list that can take X amount of coldkey dests + pub fn do_schedule_coldkey_swap( old_coldkey: &T::AccountId, new_coldkey: &T::AccountId, ) -> DispatchResult { + + // TODO: Check minimum amount of TAO // Catch spurious swaps. ensure!(*old_coldkey != *new_coldkey, Error::::SameColdkey); @@ -234,14 +264,11 @@ impl Pallet { // Update the list of coldkeys arbitrate on this block. let mut key_to_arbitrate_on_this_block: Vec = - ColdkeysToArbitrateAtBlock::::get(arbitration_block); + ColdkeysToSwapAtBlock::::get(arbitration_block); if !key_to_arbitrate_on_this_block.contains(&old_coldkey.clone()) { key_to_arbitrate_on_this_block.push(old_coldkey.clone()); } - ColdkeysToArbitrateAtBlock::::insert( - arbitration_block, - key_to_arbitrate_on_this_block, - ); + ColdkeysToSwapAtBlock::::insert(arbitration_block, key_to_arbitrate_on_this_block); } // Return true. @@ -257,17 +284,16 @@ impl Pallet { /// # Returns /// /// * `Weight` - The total weight consumed by the operation. - pub fn arbitrate_coldkeys_this_block() -> Result { + pub fn swap_coldkeys_this_block() -> Result { let mut weight = frame_support::weights::Weight::from_parts(0, 0); // Get the block number let current_block: u64 = Self::get_current_block_as_u64(); - log::debug!("Arbitrating coldkeys for block: {:?}", current_block); + log::debug!("Swapping coldkeys for block: {:?}", current_block); // Get the coldkeys to swap here and then remove them. - let source_coldkeys: Vec = - ColdkeysToArbitrateAtBlock::::get(current_block); - ColdkeysToArbitrateAtBlock::::remove(current_block); + let source_coldkeys: Vec = ColdkeysToSwapAtBlock::::get(current_block); + ColdkeysToSwapAtBlock::::remove(current_block); weight = weight.saturating_add(T::DbWeight::get().reads_writes(1, 1)); // Iterate over all keys in swap and call perform_swap_coldkey for each diff --git a/pallets/subtensor/tests/staking.rs b/pallets/subtensor/tests/staking.rs index 83c318dbb..80e49de88 100644 --- a/pallets/subtensor/tests/staking.rs +++ b/pallets/subtensor/tests/staking.rs @@ -1,5 +1,8 @@ #![allow(clippy::unwrap_used)] +use frame_support::pallet_prelude::{ + InvalidTransaction, TransactionValidity, TransactionValidityError, +}; use frame_support::{assert_err, assert_noop, assert_ok, traits::Currency}; use frame_system::Config; mod mock; @@ -8,6 +11,7 @@ use frame_support::sp_runtime::DispatchError; use mock::*; use pallet_subtensor::*; use sp_core::{H256, U256}; +use sp_runtime::traits::SignedExtension; /*********************************************************** staking::add_stake() tests @@ -3159,7 +3163,7 @@ fn test_arbitrated_coldkey_swap_success() { new_test_ext(1).execute_with(|| { let (current_coldkey, hotkey, new_coldkey) = setup_test_environment(); - assert_ok!(SubtensorModule::do_schedule_arbitrated_coldkey_swap( + assert_ok!(SubtensorModule::do_schedule_coldkey_swap( ¤t_coldkey, &new_coldkey )); @@ -3174,25 +3178,25 @@ fn test_arbitrated_coldkey_swap_success() { let drain_block: u64 = 7200 * 4 + 1; log::info!( - "ColdkeysToArbitrateAtBlock before scheduling: {:?}", - pallet_subtensor::ColdkeysToArbitrateAtBlock::::get(drain_block) + "ColdkeysToSwapAtBlock before scheduling: {:?}", + pallet_subtensor::ColdkeysToSwapAtBlock::::get(drain_block) ); assert_eq!( - pallet_subtensor::ColdkeysToArbitrateAtBlock::::get(drain_block), + pallet_subtensor::ColdkeysToSwapAtBlock::::get(drain_block), vec![current_coldkey] ); log::info!("Drain block set correctly: {:?}", drain_block); log::info!( "Drain block {:?}", - pallet_subtensor::ColdkeysToArbitrateAtBlock::::get(drain_block) + pallet_subtensor::ColdkeysToSwapAtBlock::::get(drain_block) ); // Make 7200 * 4 blocks pass run_to_block(drain_block); // Run unstaking - SubtensorModule::arbitrate_coldkeys_this_block().unwrap(); + SubtensorModule::swap_coldkeys_this_block().unwrap(); log::info!( "Arbitrated coldkeys for block: {:?}", SubtensorModule::get_current_block_as_u64() @@ -3219,10 +3223,7 @@ fn test_arbitrated_coldkey_swap_same_coldkey() { let (current_coldkey, _hotkey, _) = setup_test_environment(); assert_noop!( - SubtensorModule::do_schedule_arbitrated_coldkey_swap( - ¤t_coldkey, - ¤t_coldkey - ), + SubtensorModule::do_schedule_coldkey_swap(¤t_coldkey, ¤t_coldkey), Error::::SameColdkey ); }); @@ -3262,8 +3263,7 @@ fn test_arbitrated_coldkey_swap_no_balance() { assert_eq!(Balances::total_balance(&new_coldkey), 0); // Try to unstake and transfer - let result = - SubtensorModule::do_schedule_arbitrated_coldkey_swap(¤t_coldkey, &new_coldkey); + let result = SubtensorModule::do_schedule_coldkey_swap(¤t_coldkey, &new_coldkey); // Print the result log::info!("Result of arbitrated_coldkey_swap: {:?}", result); @@ -3328,7 +3328,7 @@ fn test_arbitrated_coldkey_swap_with_no_stake() { assert_eq!(Balances::total_balance(&new_coldkey), 0); // Perform unstake and transfer - assert_ok!(SubtensorModule::do_schedule_arbitrated_coldkey_swap( + assert_ok!(SubtensorModule::do_schedule_coldkey_swap( ¤t_coldkey, &new_coldkey )); @@ -3372,7 +3372,7 @@ fn test_arbitrated_coldkey_swap_with_multiple_stakes() { 300 )); - assert_ok!(SubtensorModule::do_schedule_arbitrated_coldkey_swap( + assert_ok!(SubtensorModule::do_schedule_coldkey_swap( ¤t_coldkey, &new_coldkey )); @@ -3415,13 +3415,13 @@ fn test_arbitrated_coldkey_swap_multiple_arbitrations() { register_ok_neuron(1, hotkey, coldkey, 0); // Owner schedules a swap for themselves. - assert_ok!(SubtensorModule::do_schedule_arbitrated_coldkey_swap( + assert_ok!(SubtensorModule::do_schedule_coldkey_swap( &coldkey, &new_coldkey1 )); // Attacker schedules the second swap for themselves. - assert_ok!(SubtensorModule::do_schedule_arbitrated_coldkey_swap( + assert_ok!(SubtensorModule::do_schedule_coldkey_swap( &coldkey, &new_coldkey2 )); @@ -3434,7 +3434,7 @@ fn test_arbitrated_coldkey_swap_multiple_arbitrations() { // Check that we are arbitrating next block. assert_eq!( - pallet_subtensor::ColdkeysToArbitrateAtBlock::::get( + pallet_subtensor::ColdkeysToSwapAtBlock::::get( SubtensorModule::get_current_block_as_u64() + 1 ), vec![coldkey] @@ -3460,7 +3460,7 @@ fn test_arbitrated_coldkey_swap_multiple_arbitrations() { // Arbitration has been pushed back but there are no keys to add to the list to arbitrate. assert_eq!( - pallet_subtensor::ColdkeysToArbitrateAtBlock::::get( + pallet_subtensor::ColdkeysToSwapAtBlock::::get( SubtensorModule::get_current_block_as_u64() + 1 ), vec![] @@ -3482,7 +3482,7 @@ fn test_arbitrated_coldkey_swap_multiple_arbitrations() { assert!(!SubtensorModule::coldkey_in_arbitration(&coldkey)); // Owner schedules a swap for themselves leys go back into arbitration. - assert_ok!(SubtensorModule::do_schedule_arbitrated_coldkey_swap( + assert_ok!(SubtensorModule::do_schedule_coldkey_swap( &coldkey, &new_coldkey1 )); @@ -3514,19 +3514,19 @@ fn test_arbitrated_coldkey_swap_existing_destination() { let another_coldkey = U256::from(4); // Schedule a swap to new_coldkey - assert_ok!(SubtensorModule::do_schedule_arbitrated_coldkey_swap( + assert_ok!(SubtensorModule::do_schedule_coldkey_swap( ¤t_coldkey, &new_coldkey )); // Attempt to schedule a swap to the same new_coldkey again assert_noop!( - SubtensorModule::do_schedule_arbitrated_coldkey_swap(¤t_coldkey, &new_coldkey), + SubtensorModule::do_schedule_coldkey_swap(¤t_coldkey, &new_coldkey), Error::::DuplicateColdkey ); // Schedule a swap to another_coldkey - assert_ok!(SubtensorModule::do_schedule_arbitrated_coldkey_swap( + assert_ok!(SubtensorModule::do_schedule_coldkey_swap( ¤t_coldkey, &another_coldkey )); @@ -3534,7 +3534,7 @@ fn test_arbitrated_coldkey_swap_existing_destination() { // Attempt to schedule a third swap let third_coldkey = U256::from(5); assert_noop!( - SubtensorModule::do_schedule_arbitrated_coldkey_swap(¤t_coldkey, &third_coldkey), + SubtensorModule::do_schedule_coldkey_swap(¤t_coldkey, &third_coldkey), Error::::ColdkeyIsInArbitration ); }); @@ -3547,13 +3547,13 @@ fn test_arbitration_period_extension() { let another_coldkey = U256::from(4); // Schedule a swap to new_coldkey - assert_ok!(SubtensorModule::do_schedule_arbitrated_coldkey_swap( + assert_ok!(SubtensorModule::do_schedule_coldkey_swap( ¤t_coldkey, &new_coldkey )); // Schedule a swap to another_coldkey - assert_ok!(SubtensorModule::do_schedule_arbitrated_coldkey_swap( + assert_ok!(SubtensorModule::do_schedule_coldkey_swap( ¤t_coldkey, &another_coldkey )); @@ -3595,11 +3595,11 @@ fn test_concurrent_arbitrated_coldkey_swaps() { SubtensorModule::add_balance_to_coldkey_account(&coldkey2, 1000); // Schedule swaps for both coldkeys - assert_ok!(SubtensorModule::do_schedule_arbitrated_coldkey_swap( + assert_ok!(SubtensorModule::do_schedule_coldkey_swap( &coldkey1, &new_coldkey1 )); - assert_ok!(SubtensorModule::do_schedule_arbitrated_coldkey_swap( + assert_ok!(SubtensorModule::do_schedule_coldkey_swap( &coldkey2, &new_coldkey2 )); @@ -3609,10 +3609,185 @@ fn test_concurrent_arbitrated_coldkey_swaps() { run_to_block(drain_block); // Run arbitration - SubtensorModule::arbitrate_coldkeys_this_block().unwrap(); + SubtensorModule::swap_coldkeys_this_block().unwrap(); // Check that the balances have been transferred correctly assert_eq!(SubtensorModule::get_coldkey_balance(&new_coldkey1), 1000); assert_eq!(SubtensorModule::get_coldkey_balance(&new_coldkey2), 1000); }); } + +#[test] +fn test_get_remaining_arbitration_period() { + new_test_ext(1).execute_with(|| { + let coldkey_account_id = U256::from(12345); // arbitrary coldkey + let new_coldkey_account_id = U256::from(54321); // arbitrary new coldkey + + // Schedule a coldkey swap to set the arbitration block + assert_ok!(SubtensorModule::do_schedule_coldkey_swap( + &coldkey_account_id, + &new_coldkey_account_id + )); + + // Get the current block number and arbitration period + let current_block: u64 = SubtensorModule::get_current_block_as_u64(); + let arbitration_period: u64 = ArbitrationPeriod::::get(); + log::info!("arbitration_period: {:?}", arbitration_period); + let arbitration_block: u64 = current_block + arbitration_period; + log::info!("arbitration_block: {:?}", arbitration_block); + + // Check if the remaining arbitration period is correct + let remaining_period = + SubtensorModule::get_remaining_arbitration_period(&coldkey_account_id); + assert_eq!(remaining_period, arbitration_period); + + // Move the current block forward and check again + step_block(50); + let remaining_period = + SubtensorModule::get_remaining_arbitration_period(&coldkey_account_id); + assert_eq!(remaining_period, arbitration_period - 50); + + // Move the current block beyond the arbitration block and check again + step_block((arbitration_period as u16) - 50 + 1); + let remaining_period = + SubtensorModule::get_remaining_arbitration_period(&coldkey_account_id); + assert_eq!(remaining_period, 0); + }); +} + +#[test] +fn test_coldkey_in_arbitration() { + new_test_ext(1).execute_with(|| { + let coldkey_account_id = U256::from(1); + let hotkey_account_id = U256::from(2); + let new_coldkey_account_id = U256::from(3); + + // Schedule a coldkey swap to put the coldkey in arbitration + assert_ok!(SubtensorModule::do_schedule_coldkey_swap( + &coldkey_account_id, + &new_coldkey_account_id + )); + + let call = RuntimeCall::SubtensorModule(crate::Call::add_stake { + hotkey: hotkey_account_id, + amount_staked: 1000, + }); + + assert_eq!( + validate_transaction(&coldkey_account_id, &call), + Err(TransactionValidityError::Invalid(InvalidTransaction::Call)) + ); + }); +} + +#[test] +fn test_add_stake_coldkey_in_arbitration() { + new_test_ext(1).execute_with(|| { + let hotkey_account_id = U256::from(561337); + let coldkey_account_id = U256::from(61337); + let new_coldkey_account_id = U256::from(71337); + let netuid: u16 = 1; + let start_nonce: u64 = 0; + let tempo: u16 = 13; + + add_network(netuid, tempo, 0); + register_ok_neuron(netuid, hotkey_account_id, coldkey_account_id, start_nonce); + SubtensorModule::add_balance_to_coldkey_account(&coldkey_account_id, 60000); + + // Schedule a coldkey swap to put the coldkey in arbitration + assert_ok!(SubtensorModule::do_schedule_coldkey_swap( + &coldkey_account_id, + &new_coldkey_account_id + )); + + let call = RuntimeCall::SubtensorModule(crate::Call::add_stake { + hotkey: hotkey_account_id, + amount_staked: 1000, + }); + + assert_eq!( + validate_transaction(&coldkey_account_id, &call), + Err(TransactionValidityError::Invalid(InvalidTransaction::Call)) + ); + }) +} + +#[test] +fn test_remove_stake_coldkey_in_arbitration() { + new_test_ext(1).execute_with(|| { + let hotkey_account_id = U256::from(561337); + let coldkey_account_id = U256::from(61337); + let new_coldkey_account_id = U256::from(71337); + let netuid: u16 = 1; + let start_nonce: u64 = 0; + let tempo: u16 = 13; + + add_network(netuid, tempo, 0); + register_ok_neuron(netuid, hotkey_account_id, coldkey_account_id, start_nonce); + SubtensorModule::add_balance_to_coldkey_account(&coldkey_account_id, 60000); + SubtensorModule::increase_stake_on_hotkey_account(&hotkey_account_id, 1000); + + // Schedule a coldkey swap to put the coldkey in arbitration + assert_ok!(SubtensorModule::do_schedule_coldkey_swap( + &coldkey_account_id, + &new_coldkey_account_id + )); + + let call = RuntimeCall::SubtensorModule(crate::Call::remove_stake { + hotkey: hotkey_account_id, + amount_unstaked: 500, + }); + + assert_eq!( + validate_transaction(&coldkey_account_id, &call), + Err(TransactionValidityError::Invalid(InvalidTransaction::Call)) + ); + }); +} + +#[test] +fn test_add_stake_coldkey_not_in_arbitration() { + new_test_ext(1).execute_with(|| { + let hotkey_account_id = U256::from(561337); + let coldkey_account_id = U256::from(61337); + let netuid: u16 = 1; + let start_nonce: u64 = 0; + let tempo: u16 = 13; + + add_network(netuid, tempo, 0); + register_ok_neuron(netuid, hotkey_account_id, coldkey_account_id, start_nonce); + SubtensorModule::add_balance_to_coldkey_account(&coldkey_account_id, 60000); + + assert_ok!(SubtensorModule::add_stake( + <::RuntimeOrigin>::signed(coldkey_account_id), + hotkey_account_id, + 1000 + )); + }); +} + +#[test] +fn test_remove_stake_coldkey_not_in_arbitration() { + new_test_ext(1).execute_with(|| { + let hotkey_account_id = U256::from(561337); + let coldkey_account_id = U256::from(61337); + let netuid: u16 = 1; + let start_nonce: u64 = 0; + let tempo: u16 = 13; + + add_network(netuid, tempo, 0); + register_ok_neuron(netuid, hotkey_account_id, coldkey_account_id, start_nonce); + SubtensorModule::add_balance_to_coldkey_account(&coldkey_account_id, 60000); + SubtensorModule::increase_stake_on_hotkey_account(&hotkey_account_id, 1000); + + assert_ok!(SubtensorModule::remove_stake( + <::RuntimeOrigin>::signed(coldkey_account_id), + hotkey_account_id, + 500 + )); + }); +} + +fn validate_transaction(who: &AccountId, call: &RuntimeCall) -> TransactionValidity { + SubtensorSignedExtension::::new().validate(who, call, &DispatchInfo::default(), 0) +} diff --git a/runtime/src/lib.rs b/runtime/src/lib.rs index 01595a75f..7143986eb 100644 --- a/runtime/src/lib.rs +++ b/runtime/src/lib.rs @@ -312,7 +312,7 @@ impl Contains for SafeModeWhitelistedCalls { | RuntimeCall::SafeMode(_) | RuntimeCall::Timestamp(_) | RuntimeCall::SubtensorModule( - pallet_subtensor::Call::do_schedule_arbitrated_coldkey_swap { .. } + pallet_subtensor::Call::schedule_coldkey_swap { .. } | pallet_subtensor::Call::set_weights { .. } | pallet_subtensor::Call::set_root_weights { .. } ) diff --git a/scripts/benchmark.sh b/scripts/benchmark.sh index 4cba2ae7a..8f54fa54a 100755 --- a/scripts/benchmark.sh +++ b/scripts/benchmark.sh @@ -40,7 +40,7 @@ $BIN_PATH build-spec --disable-default-bootnode --raw --chain local >$TMP_SPEC # Run benchmark $BIN_PATH benchmark pallet \ --chain=$TMP_SPEC \ ---pallet pallet-subtensor --extrinsic 'schedule_arbitrated_coldkey_swap' \ +--pallet pallet-subtensor --extrinsic 'schedule_coldkey_swap' \ --output $OUTPUT_FILE rm $TMP_SPEC From 507e8cf0fd7a2c8d87cbf1043ce8cfa78aa602b5 Mon Sep 17 00:00:00 2001 From: Samuel Dare Date: Sun, 7 Jul 2024 22:13:12 +0400 Subject: [PATCH 17/51] chore: lints --- pallets/subtensor/src/lib.rs | 2 +- pallets/subtensor/src/swap.rs | 5 ++--- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/pallets/subtensor/src/lib.rs b/pallets/subtensor/src/lib.rs index 3059ee84d..b05a67047 100644 --- a/pallets/subtensor/src/lib.rs +++ b/pallets/subtensor/src/lib.rs @@ -2342,7 +2342,7 @@ where ) -> TransactionValidity { if Pallet::::coldkey_in_arbitration(who) { return Err(TransactionValidityError::Invalid( - InvalidTransaction::Call.into(), + InvalidTransaction::Call, )); } diff --git a/pallets/subtensor/src/swap.rs b/pallets/subtensor/src/swap.rs index 48a3ca1cc..aa99a649b 100644 --- a/pallets/subtensor/src/swap.rs +++ b/pallets/subtensor/src/swap.rs @@ -226,13 +226,12 @@ impl Pallet { /// The list of coldkeys to arbitrate at the arbitration block is updated. // TOOD: // Check minimum amount of TAO - // Add POW functionality / Move Destination Coldkeys to a list that can take X amount of coldkey dests + // Add POW functionality / Move Destination Coldkeys to a list that can take X amount of coldkey dests pub fn do_schedule_coldkey_swap( old_coldkey: &T::AccountId, new_coldkey: &T::AccountId, ) -> DispatchResult { - - // TODO: Check minimum amount of TAO + // TODO: Check minimum amount of TAO // Catch spurious swaps. ensure!(*old_coldkey != *new_coldkey, Error::::SameColdkey); From 0bcdd2616a1ffcf20283e8aa963c722899433ab4 Mon Sep 17 00:00:00 2001 From: Samuel Dare Date: Sun, 7 Jul 2024 22:56:30 +0400 Subject: [PATCH 18/51] feat: signed extensions , reduce arbitration time --- pallets/subtensor/src/lib.rs | 34 ++++++++++++--- pallets/subtensor/tests/staking.rs | 70 ++++++++++-------------------- 2 files changed, 50 insertions(+), 54 deletions(-) diff --git a/pallets/subtensor/src/lib.rs b/pallets/subtensor/src/lib.rs index b05a67047..95ca4d54a 100644 --- a/pallets/subtensor/src/lib.rs +++ b/pallets/subtensor/src/lib.rs @@ -18,6 +18,7 @@ use frame_support::{ use codec::{Decode, Encode}; use frame_support::sp_runtime::transaction_validity::InvalidTransaction; use frame_support::sp_runtime::transaction_validity::ValidTransaction; +use pallet_balances::Call as BalancesCall; use scale_info::TypeInfo; use sp_runtime::{ traits::{DispatchInfoOf, Dispatchable, PostDispatchInfoOf, SignedExtension}, @@ -392,7 +393,7 @@ pub mod pallet { #[pallet::type_value] /// Default stake interval. pub fn DefaultArbitrationPeriod() -> u64 { - 7200 * 4 + 7200 * 3 } #[pallet::storage] // ---- StorageItem Global Used Work. pub type ArbitrationPeriod = @@ -2317,10 +2318,12 @@ impl sp_std::fmt::Debug for SubtensorSignedE } } -impl SignedExtension for SubtensorSignedExtension +impl SignedExtension + for SubtensorSignedExtension where T::RuntimeCall: Dispatchable, ::RuntimeCall: IsSubType>, + ::RuntimeCall: IsSubType>, { const IDENTIFIER: &'static str = "SubtensorSignedExtension"; @@ -2340,12 +2343,19 @@ where _info: &DispatchInfoOf, _len: usize, ) -> TransactionValidity { - if Pallet::::coldkey_in_arbitration(who) { - return Err(TransactionValidityError::Invalid( - InvalidTransaction::Call, - )); + // Check if the call is one of the balance transfer types we want to reject + if let Some(balances_call) = call.is_sub_type() { + match balances_call { + BalancesCall::transfer_allow_death { .. } + | BalancesCall::transfer_keep_alive { .. } + | BalancesCall::transfer_all { .. } => { + if Pallet::::coldkey_in_arbitration(who) { + return Err(TransactionValidityError::Invalid(InvalidTransaction::Call)); + } + } + _ => {} // Other Balances calls are allowed + } } - match call.is_sub_type() { Some(Call::commit_weights { netuid, .. }) => { if Self::check_weights_min_stake(who) { @@ -2422,6 +2432,16 @@ where priority: Self::get_priority_vanilla(), ..Default::default() }), + Some(Call::dissolve_network { .. }) => { + if Pallet::::coldkey_in_arbitration(who) { + Err(TransactionValidityError::Invalid(InvalidTransaction::Call)) + } else { + Ok(ValidTransaction { + priority: Self::get_priority_vanilla(), + ..Default::default() + }) + } + } _ => Ok(ValidTransaction { priority: Self::get_priority_vanilla(), ..Default::default() diff --git a/pallets/subtensor/tests/staking.rs b/pallets/subtensor/tests/staking.rs index 80e49de88..0a9e88117 100644 --- a/pallets/subtensor/tests/staking.rs +++ b/pallets/subtensor/tests/staking.rs @@ -9,6 +9,7 @@ mod mock; use frame_support::dispatch::{DispatchClass, DispatchInfo, GetDispatchInfo, Pays}; use frame_support::sp_runtime::DispatchError; use mock::*; +use pallet_balances::Call as BalancesCall; use pallet_subtensor::*; use sp_core::{H256, U256}; use sp_runtime::traits::SignedExtension; @@ -3175,7 +3176,7 @@ fn test_arbitrated_coldkey_swap_success() { ); // Check that drain block is set correctly - let drain_block: u64 = 7200 * 4 + 1; + let drain_block: u64 = 7200 * 3 + 1; log::info!( "ColdkeysToSwapAtBlock before scheduling: {:?}", @@ -3656,21 +3657,25 @@ fn test_get_remaining_arbitration_period() { } #[test] -fn test_coldkey_in_arbitration() { +fn test_transfer_coldkey_in_arbitration() { new_test_ext(1).execute_with(|| { let coldkey_account_id = U256::from(1); - let hotkey_account_id = U256::from(2); + let recipient_account_id = U256::from(2); let new_coldkey_account_id = U256::from(3); + // Add balance to coldkey + SubtensorModule::add_balance_to_coldkey_account(&coldkey_account_id, 60000); + // Schedule a coldkey swap to put the coldkey in arbitration assert_ok!(SubtensorModule::do_schedule_coldkey_swap( &coldkey_account_id, &new_coldkey_account_id )); - let call = RuntimeCall::SubtensorModule(crate::Call::add_stake { - hotkey: hotkey_account_id, - amount_staked: 1000, + // Try to transfer balance + let call = RuntimeCall::Balances(BalancesCall::transfer_allow_death { + dest: recipient_account_id.into(), + value: 1000, }); assert_eq!( @@ -3705,10 +3710,8 @@ fn test_add_stake_coldkey_in_arbitration() { amount_staked: 1000, }); - assert_eq!( - validate_transaction(&coldkey_account_id, &call), - Err(TransactionValidityError::Invalid(InvalidTransaction::Call)) - ); + // This should now be Ok + assert!(validate_transaction(&coldkey_account_id, &call).is_ok()); }) } @@ -3738,53 +3741,26 @@ fn test_remove_stake_coldkey_in_arbitration() { amount_unstaked: 500, }); - assert_eq!( - validate_transaction(&coldkey_account_id, &call), - Err(TransactionValidityError::Invalid(InvalidTransaction::Call)) - ); + // This should now be Ok + assert!(validate_transaction(&coldkey_account_id, &call).is_ok()); }); } #[test] -fn test_add_stake_coldkey_not_in_arbitration() { +fn test_transfer_coldkey_not_in_arbitration() { new_test_ext(1).execute_with(|| { - let hotkey_account_id = U256::from(561337); let coldkey_account_id = U256::from(61337); - let netuid: u16 = 1; - let start_nonce: u64 = 0; - let tempo: u16 = 13; + let recipient_account_id = U256::from(71337); - add_network(netuid, tempo, 0); - register_ok_neuron(netuid, hotkey_account_id, coldkey_account_id, start_nonce); SubtensorModule::add_balance_to_coldkey_account(&coldkey_account_id, 60000); - assert_ok!(SubtensorModule::add_stake( - <::RuntimeOrigin>::signed(coldkey_account_id), - hotkey_account_id, - 1000 - )); - }); -} - -#[test] -fn test_remove_stake_coldkey_not_in_arbitration() { - new_test_ext(1).execute_with(|| { - let hotkey_account_id = U256::from(561337); - let coldkey_account_id = U256::from(61337); - let netuid: u16 = 1; - let start_nonce: u64 = 0; - let tempo: u16 = 13; - - add_network(netuid, tempo, 0); - register_ok_neuron(netuid, hotkey_account_id, coldkey_account_id, start_nonce); - SubtensorModule::add_balance_to_coldkey_account(&coldkey_account_id, 60000); - SubtensorModule::increase_stake_on_hotkey_account(&hotkey_account_id, 1000); + let call = RuntimeCall::Balances(BalancesCall::transfer_allow_death { + dest: recipient_account_id.into(), + value: 1000, + }); - assert_ok!(SubtensorModule::remove_stake( - <::RuntimeOrigin>::signed(coldkey_account_id), - hotkey_account_id, - 500 - )); + // This should be Ok + assert!(validate_transaction(&coldkey_account_id, &call).is_ok()); }); } From cc9c0946c6d3a9ae57f2ad5bd442ef98e5916a78 Mon Sep 17 00:00:00 2001 From: Samuel Dare Date: Mon, 8 Jul 2024 02:19:29 +0400 Subject: [PATCH 19/51] feat: pow coldkey swaps ,todo fix tests , dont clear map during abritration --- pallets/subtensor/src/errors.rs | 2 + pallets/subtensor/src/lib.rs | 5 +- pallets/subtensor/src/swap.rs | 66 ++++++--- pallets/subtensor/tests/staking.rs | 219 +++++++++++++++++++++++------ 4 files changed, 228 insertions(+), 64 deletions(-) diff --git a/pallets/subtensor/src/errors.rs b/pallets/subtensor/src/errors.rs index cc1aa2055..3bfae3cef 100644 --- a/pallets/subtensor/src/errors.rs +++ b/pallets/subtensor/src/errors.rs @@ -152,5 +152,7 @@ mod errors { DuplicateColdkey, /// Error thrown on a coldkey swap. SwapError, + /// Insufficient Balance to Schedule coldkey swap + InsufficientBalanceToPerformColdkeySwap, } } diff --git a/pallets/subtensor/src/lib.rs b/pallets/subtensor/src/lib.rs index 95ca4d54a..96f3798da 100644 --- a/pallets/subtensor/src/lib.rs +++ b/pallets/subtensor/src/lib.rs @@ -2082,10 +2082,13 @@ pub mod pallet { pub fn schedule_coldkey_swap( origin: OriginFor, new_coldkey: T::AccountId, + work: Vec, + block_number: u64, + nonce: u64, ) -> DispatchResult { // Attain the calling coldkey from the origin. let old_coldkey: T::AccountId = ensure_signed(origin)?; - Self::do_schedule_coldkey_swap(&old_coldkey, &new_coldkey) + Self::do_schedule_coldkey_swap(&old_coldkey, &new_coldkey, work, block_number, nonce) } // ---- SUDO ONLY FUNCTIONS ------------------------------------------------------------ diff --git a/pallets/subtensor/src/swap.rs b/pallets/subtensor/src/swap.rs index aa99a649b..0187aa436 100644 --- a/pallets/subtensor/src/swap.rs +++ b/pallets/subtensor/src/swap.rs @@ -2,7 +2,7 @@ use super::*; use frame_support::traits::fungible::Mutate; use frame_support::traits::tokens::Preservation; use frame_support::{storage::IterableStorageDoubleMap, weights::Weight}; -use sp_core::Get; +use sp_core::{Get, U256}; impl Pallet { /// Swaps the hotkey of a coldkey account. @@ -203,8 +203,11 @@ impl Pallet { /// /// # Arguments /// - /// * `old_coldkey` - The account ID of the old coldkey. + /// * `origin` - The origin of the call, which must be signed by the old coldkey. /// * `new_coldkey` - The account ID of the new coldkey. + /// * `work` - The proof of work submitted by the caller. + /// * `block_number` - The block number at which the work was performed. + /// * `nonce` - The nonce used for the proof of work. /// /// # Returns /// @@ -216,64 +219,85 @@ impl Pallet { /// - The old coldkey is the same as the new coldkey. /// - The new coldkey is already in the list of destination coldkeys. /// - There are already 2 destination coldkeys for the old coldkey. + /// - The old coldkey doesn't have the minimum required TAO balance. + /// - The proof of work is invalid or doesn't meet the required difficulty. /// /// # Notes /// /// This function ensures that the new coldkey is not already in the list of destination coldkeys. - /// If the list of destination coldkeys is empty or has only one entry, the new coldkey is added to the list. - /// If the list of destination coldkeys has two entries, the function returns an error. - /// If the new coldkey is added to the list for the first time, the arbitration block is set for the old coldkey. - /// The list of coldkeys to arbitrate at the arbitration block is updated. - // TOOD: - // Check minimum amount of TAO - // Add POW functionality / Move Destination Coldkeys to a list that can take X amount of coldkey dests + /// It also checks for a minimum TAO balance and verifies the proof of work. + /// The difficulty of the proof of work increases exponentially with each subsequent call. pub fn do_schedule_coldkey_swap( old_coldkey: &T::AccountId, new_coldkey: &T::AccountId, + work: Vec, + block_number: u64, + nonce: u64, ) -> DispatchResult { - // TODO: Check minimum amount of TAO - // Catch spurious swaps. - ensure!(*old_coldkey != *new_coldkey, Error::::SameColdkey); + ensure!(old_coldkey != new_coldkey, Error::::SameColdkey); + + // Check minimum amount of TAO (1 TAO) + let minimum_balance: u64 = 1_000_000_000; // 1 TAO in RAO + ensure!( + Self::get_coldkey_balance(&old_coldkey) >= minimum_balance, + Error::::InsufficientBalanceToPerformColdkeySwap + ); - // Get current destination coldkeys. + // Get current destination coldkeys let mut destination_coldkeys: Vec = ColdkeySwapDestinations::::get(old_coldkey.clone()); + // Calculate difficulty based on the number of existing destination coldkeys + let difficulty = Self::calculate_pow_difficulty(destination_coldkeys.len() as u32); + let work_hash = Self::vec_to_hash(work.clone()); + ensure!( + Self::hash_meets_difficulty(&work_hash, difficulty), + Error::::InvalidDifficulty + ); + + // Verify work is the product of the nonce, the block number, and coldkey + let seal = Self::create_seal_hash(block_number, nonce, &old_coldkey); + ensure!(seal == work_hash, Error::::InvalidSeal); + // Check if the new coldkey is already in the swap wallets list ensure!( - !destination_coldkeys.contains(new_coldkey), + !destination_coldkeys.contains(&new_coldkey), Error::::DuplicateColdkey ); - // If the destinations keys are empty or have size 1 then we will add the new coldkey to the list. + // If the destinations keys are empty or have size 1 then we will add the new coldkey to the list if destination_coldkeys.is_empty() || destination_coldkeys.len() == 1_usize { - // Add this wallet to exist in the destination list. destination_coldkeys.push(new_coldkey.clone()); ColdkeySwapDestinations::::insert(old_coldkey.clone(), destination_coldkeys.clone()); } else { return Err(Error::::ColdkeyIsInArbitration.into()); } - // It is the first time we have seen this key. + // It is the first time we have seen this key if destination_coldkeys.len() == 1_usize { - // Set the arbitration block for this coldkey. + // Set the arbitration block for this coldkey let arbitration_block: u64 = Self::get_current_block_as_u64().saturating_add(ArbitrationPeriod::::get()); ColdkeyArbitrationBlock::::insert(old_coldkey.clone(), arbitration_block); - // Update the list of coldkeys arbitrate on this block. + // Update the list of coldkeys to arbitrate on this block let mut key_to_arbitrate_on_this_block: Vec = ColdkeysToSwapAtBlock::::get(arbitration_block); - if !key_to_arbitrate_on_this_block.contains(&old_coldkey.clone()) { + if !key_to_arbitrate_on_this_block.contains(&old_coldkey) { key_to_arbitrate_on_this_block.push(old_coldkey.clone()); } ColdkeysToSwapAtBlock::::insert(arbitration_block, key_to_arbitrate_on_this_block); } - // Return true. Ok(()) } + /// Calculate the proof of work difficulty based on the number of swap attempts + fn calculate_pow_difficulty(swap_attempts: u32) -> U256 { + let base_difficulty: U256 = U256::from(1_000_000); // Base difficulty + base_difficulty * U256::from(2).pow(U256::from(swap_attempts)) + } + /// Arbitrates coldkeys that are scheduled to be swapped on this block. /// /// This function retrieves the list of coldkeys scheduled to be swapped on the current block, diff --git a/pallets/subtensor/tests/staking.rs b/pallets/subtensor/tests/staking.rs index 0a9e88117..8fa0c2e17 100644 --- a/pallets/subtensor/tests/staking.rs +++ b/pallets/subtensor/tests/staking.rs @@ -3164,9 +3164,15 @@ fn test_arbitrated_coldkey_swap_success() { new_test_ext(1).execute_with(|| { let (current_coldkey, hotkey, new_coldkey) = setup_test_environment(); + let current_block = SubtensorModule::get_current_block_as_u64(); + let (work, nonce) = generate_valid_pow(¤t_coldkey, current_block, 1_000_000); + assert_ok!(SubtensorModule::do_schedule_coldkey_swap( - ¤t_coldkey, - &new_coldkey + ¤t_coldkey.clone(), + &new_coldkey, + work, + current_block, + nonce )); // Check that ColdkeySwapDestinations is populated correctly @@ -3223,8 +3229,17 @@ fn test_arbitrated_coldkey_swap_same_coldkey() { new_test_ext(1).execute_with(|| { let (current_coldkey, _hotkey, _) = setup_test_environment(); + let current_block = SubtensorModule::get_current_block_as_u64(); + let (work, nonce) = generate_valid_pow(¤t_coldkey, current_block, 1_000_000); + assert_noop!( - SubtensorModule::do_schedule_coldkey_swap(¤t_coldkey, ¤t_coldkey), + SubtensorModule::do_schedule_coldkey_swap( + ¤t_coldkey.clone(), + ¤t_coldkey, + work, + current_block, + nonce + ), Error::::SameColdkey ); }); @@ -3263,12 +3278,28 @@ fn test_arbitrated_coldkey_swap_no_balance() { assert_eq!(Balances::total_balance(&hotkey), 0); assert_eq!(Balances::total_balance(&new_coldkey), 0); - // Try to unstake and transfer - let result = SubtensorModule::do_schedule_coldkey_swap(¤t_coldkey, &new_coldkey); + // Generate valid PoW + let current_block = SubtensorModule::get_current_block_as_u64(); + let (work, nonce) = generate_valid_pow(¤t_coldkey, current_block, 1_000_000); + + // Try to schedule coldkey swap + let result = SubtensorModule::do_schedule_coldkey_swap( + ¤t_coldkey.clone(), + &new_coldkey, + work, + current_block, + nonce, + ); // Print the result log::info!("Result of arbitrated_coldkey_swap: {:?}", result); + // Verify that the operation failed due to insufficient balance + assert_noop!( + result, + Error::::InsufficientBalanceToPerformColdkeySwap + ); + // Print final balances log::info!( "Final current_coldkey balance: {:?}", @@ -3328,14 +3359,20 @@ fn test_arbitrated_coldkey_swap_with_no_stake() { assert_eq!(Balances::total_balance(&hotkey), 0); assert_eq!(Balances::total_balance(&new_coldkey), 0); + let current_block = SubtensorModule::get_current_block_as_u64(); + let (work, nonce) = generate_valid_pow(¤t_coldkey, current_block, 1_000_000); + // Perform unstake and transfer assert_ok!(SubtensorModule::do_schedule_coldkey_swap( - ¤t_coldkey, - &new_coldkey + ¤t_coldkey.clone(), + &new_coldkey, + work, + current_block, + nonce )); // Make 7200 * 4 blocks pass - let drain_block: u64 = 7200 * 4 + 1; + let drain_block: u64 = 7200 * 3 + 1; run_to_block(drain_block); // Print final balances @@ -3373,13 +3410,19 @@ fn test_arbitrated_coldkey_swap_with_multiple_stakes() { 300 )); + let current_block = SubtensorModule::get_current_block_as_u64(); + let (work, nonce) = generate_valid_pow(¤t_coldkey, current_block, 1_000_000); + assert_ok!(SubtensorModule::do_schedule_coldkey_swap( - ¤t_coldkey, - &new_coldkey + ¤t_coldkey.clone(), + &new_coldkey, + work, + current_block, + nonce )); // Make 7200 * 4 blocks pass - let drain_block: u64 = 7200 * 4 + 1; + let drain_block: u64 = 7200 * 3 + 1; run_to_block(drain_block); // Check that all stake has been removed @@ -3415,16 +3458,26 @@ fn test_arbitrated_coldkey_swap_multiple_arbitrations() { ArbitrationPeriod::::put(1); // One block arbitration period. register_ok_neuron(1, hotkey, coldkey, 0); + let current_block = SubtensorModule::get_current_block_as_u64(); + let (work1, nonce1) = generate_valid_pow(&coldkey, current_block, 1_000_000); + let (work2, nonce2) = generate_valid_pow(&coldkey, current_block, 2_000_000); + // Owner schedules a swap for themselves. assert_ok!(SubtensorModule::do_schedule_coldkey_swap( - &coldkey, - &new_coldkey1 + &coldkey.clone(), + &new_coldkey1, + work1, + current_block, + nonce1 )); // Attacker schedules the second swap for themselves. assert_ok!(SubtensorModule::do_schedule_coldkey_swap( - &coldkey, - &new_coldkey2 + &coldkey.clone(), + &new_coldkey2, + work2, + current_block, + nonce2 )); // Both keys are added in swap destinations. @@ -3482,10 +3535,15 @@ fn test_arbitrated_coldkey_swap_multiple_arbitrations() { // Key is not in arbitration assert!(!SubtensorModule::coldkey_in_arbitration(&coldkey)); - // Owner schedules a swap for themselves leys go back into arbitration. + // Owner schedules a swap for themselves lets go back into arbitration. + let current_block = SubtensorModule::get_current_block_as_u64(); + let (work3, nonce3) = generate_valid_pow(&coldkey, current_block, 4_000_000); assert_ok!(SubtensorModule::do_schedule_coldkey_swap( - &coldkey, - &new_coldkey1 + &coldkey.clone(), + &new_coldkey1, + work3, + current_block, + nonce3 )); // Key goes back into arbitration. @@ -3514,28 +3572,51 @@ fn test_arbitrated_coldkey_swap_existing_destination() { let (current_coldkey, _hotkey, new_coldkey) = setup_test_environment(); let another_coldkey = U256::from(4); + let current_block = SubtensorModule::get_current_block_as_u64(); + let (work1, nonce1) = generate_valid_pow(¤t_coldkey, current_block, 1_000_000); + let (work2, nonce2) = generate_valid_pow(¤t_coldkey, current_block, 2_000_000); + let (work3, nonce3) = generate_valid_pow(¤t_coldkey, current_block, 4_000_000); + // Schedule a swap to new_coldkey assert_ok!(SubtensorModule::do_schedule_coldkey_swap( ¤t_coldkey, - &new_coldkey + &new_coldkey, + work1, + current_block, + nonce1 )); // Attempt to schedule a swap to the same new_coldkey again assert_noop!( - SubtensorModule::do_schedule_coldkey_swap(¤t_coldkey, &new_coldkey), + SubtensorModule::do_schedule_coldkey_swap( + ¤t_coldkey.clone(), + &new_coldkey, + work2.clone(), + current_block, + nonce2 + ), Error::::DuplicateColdkey ); // Schedule a swap to another_coldkey assert_ok!(SubtensorModule::do_schedule_coldkey_swap( - ¤t_coldkey, - &another_coldkey + ¤t_coldkey.clone(), + &another_coldkey, + work2, + current_block, + nonce2 )); // Attempt to schedule a third swap let third_coldkey = U256::from(5); assert_noop!( - SubtensorModule::do_schedule_coldkey_swap(¤t_coldkey, &third_coldkey), + SubtensorModule::do_schedule_coldkey_swap( + ¤t_coldkey.clone(), + &third_coldkey, + work3, + current_block, + nonce3 + ), Error::::ColdkeyIsInArbitration ); }); @@ -3547,16 +3628,26 @@ fn test_arbitration_period_extension() { let (current_coldkey, _hotkey, new_coldkey) = setup_test_environment(); let another_coldkey = U256::from(4); + let current_block = SubtensorModule::get_current_block_as_u64(); + let (work1, nonce1) = generate_valid_pow(¤t_coldkey, current_block, 1_000_000); + let (work2, nonce2) = generate_valid_pow(¤t_coldkey, current_block, 2_000_000); + // Schedule a swap to new_coldkey assert_ok!(SubtensorModule::do_schedule_coldkey_swap( - ¤t_coldkey, - &new_coldkey + ¤t_coldkey.clone(), + &new_coldkey, + work1, + current_block, + nonce1 )); // Schedule a swap to another_coldkey assert_ok!(SubtensorModule::do_schedule_coldkey_swap( - ¤t_coldkey, - &another_coldkey + ¤t_coldkey.clone(), + &another_coldkey, + work2, + current_block, + nonce2 )); // Check that the arbitration period is extended @@ -3595,18 +3686,27 @@ fn test_concurrent_arbitrated_coldkey_swaps() { SubtensorModule::add_balance_to_coldkey_account(&coldkey1, 1000); SubtensorModule::add_balance_to_coldkey_account(&coldkey2, 1000); + let current_block = SubtensorModule::get_current_block_as_u64(); + let (work1, nonce1) = generate_valid_pow(&coldkey1, current_block, 1_000_000); + let (work2, nonce2) = generate_valid_pow(&coldkey2, current_block, 1_000_000); + // Schedule swaps for both coldkeys assert_ok!(SubtensorModule::do_schedule_coldkey_swap( - &coldkey1, - &new_coldkey1 + &coldkey1.clone(), + &new_coldkey1, + work1, + current_block, + nonce1 )); assert_ok!(SubtensorModule::do_schedule_coldkey_swap( - &coldkey2, - &new_coldkey2 + &coldkey2.clone(), + &new_coldkey2, + work2, + current_block, + nonce2 )); - // Make 7200 * 4 blocks pass - let drain_block: u64 = 7200 * 4 + 1; + let drain_block: u64 = 7200 * 3 + 1; run_to_block(drain_block); // Run arbitration @@ -3624,10 +3724,16 @@ fn test_get_remaining_arbitration_period() { let coldkey_account_id = U256::from(12345); // arbitrary coldkey let new_coldkey_account_id = U256::from(54321); // arbitrary new coldkey + let current_block = SubtensorModule::get_current_block_as_u64(); + let (work, nonce) = generate_valid_pow(&coldkey_account_id, current_block, 1_000_000); + // Schedule a coldkey swap to set the arbitration block assert_ok!(SubtensorModule::do_schedule_coldkey_swap( - &coldkey_account_id, - &new_coldkey_account_id + &coldkey_account_id.clone(), + &new_coldkey_account_id, + work, + current_block, + nonce )); // Get the current block number and arbitration period @@ -3666,10 +3772,16 @@ fn test_transfer_coldkey_in_arbitration() { // Add balance to coldkey SubtensorModule::add_balance_to_coldkey_account(&coldkey_account_id, 60000); + let current_block = SubtensorModule::get_current_block_as_u64(); + let (work, nonce) = generate_valid_pow(&coldkey_account_id, current_block, 1_000_000); + // Schedule a coldkey swap to put the coldkey in arbitration assert_ok!(SubtensorModule::do_schedule_coldkey_swap( - &coldkey_account_id, - &new_coldkey_account_id + &coldkey_account_id.clone(), + &new_coldkey_account_id, + work, + current_block, + nonce )); // Try to transfer balance @@ -3699,12 +3811,17 @@ fn test_add_stake_coldkey_in_arbitration() { register_ok_neuron(netuid, hotkey_account_id, coldkey_account_id, start_nonce); SubtensorModule::add_balance_to_coldkey_account(&coldkey_account_id, 60000); + let current_block = SubtensorModule::get_current_block_as_u64(); + let (work, nonce) = generate_valid_pow(&coldkey_account_id, current_block, 1_000_000); + // Schedule a coldkey swap to put the coldkey in arbitration assert_ok!(SubtensorModule::do_schedule_coldkey_swap( - &coldkey_account_id, - &new_coldkey_account_id + &coldkey_account_id.clone(), + &new_coldkey_account_id, + work, + current_block, + nonce )); - let call = RuntimeCall::SubtensorModule(crate::Call::add_stake { hotkey: hotkey_account_id, amount_staked: 1000, @@ -3730,10 +3847,16 @@ fn test_remove_stake_coldkey_in_arbitration() { SubtensorModule::add_balance_to_coldkey_account(&coldkey_account_id, 60000); SubtensorModule::increase_stake_on_hotkey_account(&hotkey_account_id, 1000); + let current_block = SubtensorModule::get_current_block_as_u64(); + let (work, nonce) = generate_valid_pow(&coldkey_account_id, current_block, 1_000_000); + // Schedule a coldkey swap to put the coldkey in arbitration assert_ok!(SubtensorModule::do_schedule_coldkey_swap( - &coldkey_account_id, - &new_coldkey_account_id + &coldkey_account_id.clone(), + &new_coldkey_account_id, + work, + current_block, + nonce )); let call = RuntimeCall::SubtensorModule(crate::Call::remove_stake { @@ -3767,3 +3890,15 @@ fn test_transfer_coldkey_not_in_arbitration() { fn validate_transaction(who: &AccountId, call: &RuntimeCall) -> TransactionValidity { SubtensorSignedExtension::::new().validate(who, call, &DispatchInfo::default(), 0) } + +// Helper function to generate valid PoW +fn generate_valid_pow(coldkey: &AccountId, block_number: u64, difficulty: u32) -> (Vec, u64) { + let mut nonce: u64 = 0; + loop { + let work = SubtensorModule::create_seal_hash(block_number, nonce, coldkey); + if SubtensorModule::hash_meets_difficulty(&work, difficulty.into()) { + return (work.as_bytes().to_vec(), nonce); + } + nonce += 1; + } +} From 60f371347dddc808adc2a4232574e59a24d2a024 Mon Sep 17 00:00:00 2001 From: open-junius Date: Mon, 8 Jul 2024 11:03:13 +0800 Subject: [PATCH 20/51] fix unit tests --- pallets/subtensor/src/lib.rs | 3 + pallets/subtensor/src/swap.rs | 10 ++-- pallets/subtensor/tests/staking.rs | 91 ++++++++++++++++++++++++------ 3 files changed, 83 insertions(+), 21 deletions(-) diff --git a/pallets/subtensor/src/lib.rs b/pallets/subtensor/src/lib.rs index 96f3798da..489e0ffe3 100644 --- a/pallets/subtensor/src/lib.rs +++ b/pallets/subtensor/src/lib.rs @@ -84,6 +84,9 @@ pub mod pallet { /// order of migrations. (i.e. always increasing) const STORAGE_VERSION: StorageVersion = StorageVersion::new(6); + /// Minimum balance required to perform a coldkey swap + pub const MIN_BALANCE_TO_PERFORM_COLDKEY_SWAP: u64 = 1_000_000_000; // 1 TAO in RAO + #[pallet::pallet] #[pallet::without_storage_info] #[pallet::storage_version(STORAGE_VERSION)] diff --git a/pallets/subtensor/src/swap.rs b/pallets/subtensor/src/swap.rs index 0187aa436..f5b10d824 100644 --- a/pallets/subtensor/src/swap.rs +++ b/pallets/subtensor/src/swap.rs @@ -1,4 +1,5 @@ use super::*; +use crate::MIN_BALANCE_TO_PERFORM_COLDKEY_SWAP; use frame_support::traits::fungible::Mutate; use frame_support::traits::tokens::Preservation; use frame_support::{storage::IterableStorageDoubleMap, weights::Weight}; @@ -237,9 +238,8 @@ impl Pallet { ensure!(old_coldkey != new_coldkey, Error::::SameColdkey); // Check minimum amount of TAO (1 TAO) - let minimum_balance: u64 = 1_000_000_000; // 1 TAO in RAO ensure!( - Self::get_coldkey_balance(&old_coldkey) >= minimum_balance, + Self::get_coldkey_balance(old_coldkey) >= MIN_BALANCE_TO_PERFORM_COLDKEY_SWAP, Error::::InsufficientBalanceToPerformColdkeySwap ); @@ -256,12 +256,12 @@ impl Pallet { ); // Verify work is the product of the nonce, the block number, and coldkey - let seal = Self::create_seal_hash(block_number, nonce, &old_coldkey); + let seal = Self::create_seal_hash(block_number, nonce, old_coldkey); ensure!(seal == work_hash, Error::::InvalidSeal); // Check if the new coldkey is already in the swap wallets list ensure!( - !destination_coldkeys.contains(&new_coldkey), + !destination_coldkeys.contains(new_coldkey), Error::::DuplicateColdkey ); @@ -283,7 +283,7 @@ impl Pallet { // Update the list of coldkeys to arbitrate on this block let mut key_to_arbitrate_on_this_block: Vec = ColdkeysToSwapAtBlock::::get(arbitration_block); - if !key_to_arbitrate_on_this_block.contains(&old_coldkey) { + if !key_to_arbitrate_on_this_block.contains(old_coldkey) { key_to_arbitrate_on_this_block.push(old_coldkey.clone()); } ColdkeysToSwapAtBlock::::insert(arbitration_block, key_to_arbitrate_on_this_block); diff --git a/pallets/subtensor/tests/staking.rs b/pallets/subtensor/tests/staking.rs index 8fa0c2e17..cb143d438 100644 --- a/pallets/subtensor/tests/staking.rs +++ b/pallets/subtensor/tests/staking.rs @@ -3166,7 +3166,10 @@ fn test_arbitrated_coldkey_swap_success() { let current_block = SubtensorModule::get_current_block_as_u64(); let (work, nonce) = generate_valid_pow(¤t_coldkey, current_block, 1_000_000); - + SubtensorModule::add_balance_to_coldkey_account( + ¤t_coldkey, + MIN_BALANCE_TO_PERFORM_COLDKEY_SWAP, + ); assert_ok!(SubtensorModule::do_schedule_coldkey_swap( ¤t_coldkey.clone(), &new_coldkey, @@ -3219,7 +3222,10 @@ fn test_arbitrated_coldkey_swap_success() { ); // Check that the balance has been transferred to the new coldkey - assert_eq!(SubtensorModule::get_coldkey_balance(&new_coldkey), 500); // The new key as the 500 + assert_eq!( + SubtensorModule::get_coldkey_balance(&new_coldkey), + MIN_BALANCE_TO_PERFORM_COLDKEY_SWAP + 500 + ); // The new key as the 500 }); } @@ -3337,8 +3343,7 @@ fn test_arbitrated_coldkey_swap_with_no_stake() { register_ok_neuron(1, hotkey, current_coldkey, 0); // Add balance to the current coldkey without staking - let initial_balance = 500; - Balances::make_free_balance_be(¤t_coldkey, initial_balance); + Balances::make_free_balance_be(¤t_coldkey, MIN_BALANCE_TO_PERFORM_COLDKEY_SWAP); // Print initial balances log::info!( @@ -3355,7 +3360,10 @@ fn test_arbitrated_coldkey_swap_with_no_stake() { ); // Ensure initial balances are correct - assert_eq!(Balances::total_balance(¤t_coldkey), initial_balance); + assert_eq!( + Balances::total_balance(¤t_coldkey), + MIN_BALANCE_TO_PERFORM_COLDKEY_SWAP + ); assert_eq!(Balances::total_balance(&hotkey), 0); assert_eq!(Balances::total_balance(&new_coldkey), 0); @@ -3390,7 +3398,10 @@ fn test_arbitrated_coldkey_swap_with_no_stake() { ); // Check that the balance has been transferred to the new coldkey - assert_eq!(Balances::total_balance(&new_coldkey), initial_balance); + assert_eq!( + Balances::total_balance(&new_coldkey), + MIN_BALANCE_TO_PERFORM_COLDKEY_SWAP + ); assert_eq!(Balances::total_balance(¤t_coldkey), 0); }); } @@ -3402,6 +3413,10 @@ fn test_arbitrated_coldkey_swap_with_multiple_stakes() { let (current_coldkey, hotkey, new_coldkey) = setup_test_environment(); SubtensorModule::set_target_stakes_per_interval(10); + SubtensorModule::add_balance_to_coldkey_account( + ¤t_coldkey, + MIN_BALANCE_TO_PERFORM_COLDKEY_SWAP, + ); // Add more stake assert_ok!(SubtensorModule::add_stake( @@ -3435,7 +3450,10 @@ fn test_arbitrated_coldkey_swap_with_multiple_stakes() { ); // Check that the full balance has been transferred to the new coldkey - assert_eq!(SubtensorModule::get_coldkey_balance(&new_coldkey), 200); + assert_eq!( + SubtensorModule::get_coldkey_balance(&new_coldkey), + MIN_BALANCE_TO_PERFORM_COLDKEY_SWAP + 200 + ); // Check that the full balance has been transferred to the new coldkey assert_eq!(SubtensorModule::get_coldkey_balance(¤t_coldkey), 0); @@ -3454,7 +3472,10 @@ fn test_arbitrated_coldkey_swap_multiple_arbitrations() { // Setup network state. add_network(1, 0, 0); - SubtensorModule::add_balance_to_coldkey_account(&coldkey, 1000); + SubtensorModule::add_balance_to_coldkey_account( + &coldkey, + MIN_BALANCE_TO_PERFORM_COLDKEY_SWAP, + ); ArbitrationPeriod::::put(1); // One block arbitration period. register_ok_neuron(1, hotkey, coldkey, 0); @@ -3560,7 +3581,10 @@ fn test_arbitrated_coldkey_swap_multiple_arbitrations() { // New key gets amount the other keys are empty. assert_eq!(SubtensorModule::get_coldkey_balance(&coldkey), 0); - assert_eq!(SubtensorModule::get_coldkey_balance(&new_coldkey1), 1000); + assert_eq!( + SubtensorModule::get_coldkey_balance(&new_coldkey1), + MIN_BALANCE_TO_PERFORM_COLDKEY_SWAP + ); assert_eq!(SubtensorModule::get_coldkey_balance(&new_coldkey2), 0); }); } @@ -3577,6 +3601,11 @@ fn test_arbitrated_coldkey_swap_existing_destination() { let (work2, nonce2) = generate_valid_pow(¤t_coldkey, current_block, 2_000_000); let (work3, nonce3) = generate_valid_pow(¤t_coldkey, current_block, 4_000_000); + SubtensorModule::add_balance_to_coldkey_account( + ¤t_coldkey, + MIN_BALANCE_TO_PERFORM_COLDKEY_SWAP, + ); + // Schedule a swap to new_coldkey assert_ok!(SubtensorModule::do_schedule_coldkey_swap( ¤t_coldkey, @@ -3631,6 +3660,10 @@ fn test_arbitration_period_extension() { let current_block = SubtensorModule::get_current_block_as_u64(); let (work1, nonce1) = generate_valid_pow(¤t_coldkey, current_block, 1_000_000); let (work2, nonce2) = generate_valid_pow(¤t_coldkey, current_block, 2_000_000); + SubtensorModule::add_balance_to_coldkey_account( + ¤t_coldkey, + MIN_BALANCE_TO_PERFORM_COLDKEY_SWAP, + ); // Schedule a swap to new_coldkey assert_ok!(SubtensorModule::do_schedule_coldkey_swap( @@ -3683,8 +3716,14 @@ fn test_concurrent_arbitrated_coldkey_swaps() { register_ok_neuron(netuid2, hotkey2, coldkey2, 0); // Add balance to coldkeys - SubtensorModule::add_balance_to_coldkey_account(&coldkey1, 1000); - SubtensorModule::add_balance_to_coldkey_account(&coldkey2, 1000); + SubtensorModule::add_balance_to_coldkey_account( + &coldkey1, + MIN_BALANCE_TO_PERFORM_COLDKEY_SWAP, + ); + SubtensorModule::add_balance_to_coldkey_account( + &coldkey2, + MIN_BALANCE_TO_PERFORM_COLDKEY_SWAP, + ); let current_block = SubtensorModule::get_current_block_as_u64(); let (work1, nonce1) = generate_valid_pow(&coldkey1, current_block, 1_000_000); @@ -3713,8 +3752,14 @@ fn test_concurrent_arbitrated_coldkey_swaps() { SubtensorModule::swap_coldkeys_this_block().unwrap(); // Check that the balances have been transferred correctly - assert_eq!(SubtensorModule::get_coldkey_balance(&new_coldkey1), 1000); - assert_eq!(SubtensorModule::get_coldkey_balance(&new_coldkey2), 1000); + assert_eq!( + SubtensorModule::get_coldkey_balance(&new_coldkey1), + MIN_BALANCE_TO_PERFORM_COLDKEY_SWAP + ); + assert_eq!( + SubtensorModule::get_coldkey_balance(&new_coldkey2), + MIN_BALANCE_TO_PERFORM_COLDKEY_SWAP + ); }); } @@ -3727,6 +3772,11 @@ fn test_get_remaining_arbitration_period() { let current_block = SubtensorModule::get_current_block_as_u64(); let (work, nonce) = generate_valid_pow(&coldkey_account_id, current_block, 1_000_000); + SubtensorModule::add_balance_to_coldkey_account( + &coldkey_account_id, + MIN_BALANCE_TO_PERFORM_COLDKEY_SWAP, + ); + // Schedule a coldkey swap to set the arbitration block assert_ok!(SubtensorModule::do_schedule_coldkey_swap( &coldkey_account_id.clone(), @@ -3770,7 +3820,10 @@ fn test_transfer_coldkey_in_arbitration() { let new_coldkey_account_id = U256::from(3); // Add balance to coldkey - SubtensorModule::add_balance_to_coldkey_account(&coldkey_account_id, 60000); + SubtensorModule::add_balance_to_coldkey_account( + &coldkey_account_id, + MIN_BALANCE_TO_PERFORM_COLDKEY_SWAP, + ); let current_block = SubtensorModule::get_current_block_as_u64(); let (work, nonce) = generate_valid_pow(&coldkey_account_id, current_block, 1_000_000); @@ -3809,7 +3862,10 @@ fn test_add_stake_coldkey_in_arbitration() { add_network(netuid, tempo, 0); register_ok_neuron(netuid, hotkey_account_id, coldkey_account_id, start_nonce); - SubtensorModule::add_balance_to_coldkey_account(&coldkey_account_id, 60000); + SubtensorModule::add_balance_to_coldkey_account( + &coldkey_account_id, + MIN_BALANCE_TO_PERFORM_COLDKEY_SWAP, + ); let current_block = SubtensorModule::get_current_block_as_u64(); let (work, nonce) = generate_valid_pow(&coldkey_account_id, current_block, 1_000_000); @@ -3844,7 +3900,10 @@ fn test_remove_stake_coldkey_in_arbitration() { add_network(netuid, tempo, 0); register_ok_neuron(netuid, hotkey_account_id, coldkey_account_id, start_nonce); - SubtensorModule::add_balance_to_coldkey_account(&coldkey_account_id, 60000); + SubtensorModule::add_balance_to_coldkey_account( + &coldkey_account_id, + MIN_BALANCE_TO_PERFORM_COLDKEY_SWAP, + ); SubtensorModule::increase_stake_on_hotkey_account(&hotkey_account_id, 1000); let current_block = SubtensorModule::get_current_block_as_u64(); From be043acc273ada5192d055a585ca8e54c720050d Mon Sep 17 00:00:00 2001 From: open-junius Date: Mon, 8 Jul 2024 11:30:08 +0800 Subject: [PATCH 21/51] update runtime 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 e6ff7aa81..0afaec385 100644 --- a/runtime/src/lib.rs +++ b/runtime/src/lib.rs @@ -139,7 +139,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: 160, + spec_version: 161, impl_version: 1, apis: RUNTIME_API_VERSIONS, transaction_version: 1, From 25744e47abfc1be016046fc916469c46dd8d25ce Mon Sep 17 00:00:00 2001 From: open-junius Date: Mon, 8 Jul 2024 13:10:21 +0800 Subject: [PATCH 22/51] fix benchmark function --- pallets/subtensor/src/benchmarks.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/pallets/subtensor/src/benchmarks.rs b/pallets/subtensor/src/benchmarks.rs index cb5bbff8e..6da83c5af 100644 --- a/pallets/subtensor/src/benchmarks.rs +++ b/pallets/subtensor/src/benchmarks.rs @@ -437,6 +437,8 @@ reveal_weights { let netuid = 1u16; let tempo = 1u16; + let block_number: u64 = Subtensor::::get_current_block_as_u64(); + let nonce = 0; // Initialize the network Subtensor::::init_new_network(netuid, tempo); @@ -452,5 +454,5 @@ reveal_weights { hotkey.clone() )); - }: schedule_coldkey_swap(RawOrigin::Signed(old_coldkey.clone()), new_coldkey) + }: schedule_coldkey_swap(RawOrigin::Signed(old_coldkey.clone()), new_coldkey.clone(), vec![], block_number, nonce) } From 8f8b92c170667a02f21b118189cf6995b94082ac Mon Sep 17 00:00:00 2001 From: open-junius Date: Mon, 8 Jul 2024 13:48:06 +0800 Subject: [PATCH 23/51] add arithmetic side effects --- pallets/subtensor/src/swap.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/pallets/subtensor/src/swap.rs b/pallets/subtensor/src/swap.rs index 7a0cf1eae..cf1a45633 100644 --- a/pallets/subtensor/src/swap.rs +++ b/pallets/subtensor/src/swap.rs @@ -293,6 +293,7 @@ impl Pallet { } /// Calculate the proof of work difficulty based on the number of swap attempts + #[allow(clippy::arithmetic_side_effects)] fn calculate_pow_difficulty(swap_attempts: u32) -> U256 { let base_difficulty: U256 = U256::from(1_000_000); // Base difficulty base_difficulty * U256::from(2).pow(U256::from(swap_attempts)) From 6daad1e0c5e0973453aff91ff49a40206458410c Mon Sep 17 00:00:00 2001 From: open-junius Date: Mon, 8 Jul 2024 13:59:41 +0800 Subject: [PATCH 24/51] fix clippy in test file --- pallets/subtensor/tests/staking.rs | 6 +++--- pallets/subtensor/tests/swap.rs | 29 ----------------------------- 2 files changed, 3 insertions(+), 32 deletions(-) diff --git a/pallets/subtensor/tests/staking.rs b/pallets/subtensor/tests/staking.rs index cb143d438..4c543f586 100644 --- a/pallets/subtensor/tests/staking.rs +++ b/pallets/subtensor/tests/staking.rs @@ -3839,7 +3839,7 @@ fn test_transfer_coldkey_in_arbitration() { // Try to transfer balance let call = RuntimeCall::Balances(BalancesCall::transfer_allow_death { - dest: recipient_account_id.into(), + dest: recipient_account_id, value: 1000, }); @@ -3937,7 +3937,7 @@ fn test_transfer_coldkey_not_in_arbitration() { SubtensorModule::add_balance_to_coldkey_account(&coldkey_account_id, 60000); let call = RuntimeCall::Balances(BalancesCall::transfer_allow_death { - dest: recipient_account_id.into(), + dest: recipient_account_id, value: 1000, }); @@ -3958,6 +3958,6 @@ fn generate_valid_pow(coldkey: &AccountId, block_number: u64, difficulty: u32) - if SubtensorModule::hash_meets_difficulty(&work, difficulty.into()) { return (work.as_bytes().to_vec(), nonce); } - nonce += 1; + nonce = nonce.saturating_add(1); } } diff --git a/pallets/subtensor/tests/swap.rs b/pallets/subtensor/tests/swap.rs index d6a76ed98..80f46292b 100644 --- a/pallets/subtensor/tests/swap.rs +++ b/pallets/subtensor/tests/swap.rs @@ -1253,35 +1253,6 @@ fn test_swap_stake_for_coldkey() { }); } -#[test] -fn test_swap_owner_for_coldkey() { - new_test_ext(1).execute_with(|| { - let old_coldkey = U256::from(1); - let new_coldkey = U256::from(2); - let hotkey1 = U256::from(3); - let hotkey2 = U256::from(4); - let mut weight = Weight::zero(); - - // Initialize Owner for old_coldkey - Owner::::insert(hotkey1, old_coldkey); - Owner::::insert(hotkey2, old_coldkey); - - // Initialize OwnedHotkeys map - OwnedHotkeys::::insert(old_coldkey, vec![hotkey1, hotkey2]); - - // Perform the swap - SubtensorModule::swap_owner_for_coldkey(&old_coldkey, &new_coldkey, &mut weight); - - // Verify the swap - assert_eq!(Owner::::get(hotkey1), new_coldkey); - assert_eq!(Owner::::get(hotkey2), new_coldkey); - - // Verify weight update - let expected_weight = ::DbWeight::get().reads_writes(1, 2); - assert_eq!(weight, expected_weight); - }); -} - #[test] fn test_swap_total_hotkey_coldkey_stakes_this_interval_for_coldkey() { new_test_ext(1).execute_with(|| { From fd980b1d04ed3ffb005f9820d7edbb2fe9cd695b Mon Sep 17 00:00:00 2001 From: Samuel Dare Date: Mon, 8 Jul 2024 11:48:26 +0400 Subject: [PATCH 25/51] chore: add event --- pallets/subtensor/src/events.rs | 9 +++++++++ pallets/subtensor/src/swap.rs | 9 ++++++++- 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/pallets/subtensor/src/events.rs b/pallets/subtensor/src/events.rs index d401eebe1..913072090 100644 --- a/pallets/subtensor/src/events.rs +++ b/pallets/subtensor/src/events.rs @@ -150,5 +150,14 @@ mod events { ::AccountId, >>::Balance, }, + /// A coldkey swap has been scheduled + ColdkeySwapScheduled { + /// The account ID of the old coldkey + old_coldkey: T::AccountId, + /// The account ID of the new coldkey + new_coldkey: T::AccountId, + /// The arbitration block for the coldkey swap + arbitration_block: u64, + }, } } diff --git a/pallets/subtensor/src/swap.rs b/pallets/subtensor/src/swap.rs index cf1a45633..7bfcfb9e3 100644 --- a/pallets/subtensor/src/swap.rs +++ b/pallets/subtensor/src/swap.rs @@ -204,7 +204,7 @@ impl Pallet { /// /// # Arguments /// - /// * `origin` - The origin of the call, which must be signed by the old coldkey. + /// * `old_coldkey` - The account ID of the old coldkey. /// * `new_coldkey` - The account ID of the new coldkey. /// * `work` - The proof of work submitted by the caller. /// * `block_number` - The block number at which the work was performed. @@ -289,6 +289,13 @@ impl Pallet { ColdkeysToSwapAtBlock::::insert(arbitration_block, key_to_arbitrate_on_this_block); } + // Emit an event indicating that a coldkey swap has been scheduled + Self::deposit_event(Event::ColdkeySwapScheduled { + old_coldkey: old_coldkey.clone(), + new_coldkey: new_coldkey.clone(), + arbitration_block: ColdkeyArbitrationBlock::::get(old_coldkey), + }); + Ok(()) } From fbe19241ecffeecb9ad262859db208d99df830c5 Mon Sep 17 00:00:00 2001 From: Samuel Dare Date: Mon, 8 Jul 2024 19:26:24 +0400 Subject: [PATCH 26/51] feat: unlimited arb time --- pallets/subtensor/src/errors.rs | 2 +- pallets/subtensor/src/events.rs | 5 +++++ pallets/subtensor/src/swap.rs | 14 ++++++++------ pallets/subtensor/tests/staking.rs | 6 +++--- 4 files changed, 17 insertions(+), 10 deletions(-) diff --git a/pallets/subtensor/src/errors.rs b/pallets/subtensor/src/errors.rs index 3bfae3cef..07a16b048 100644 --- a/pallets/subtensor/src/errors.rs +++ b/pallets/subtensor/src/errors.rs @@ -151,7 +151,7 @@ mod errors { /// The new coldkey is already registered for the drain DuplicateColdkey, /// Error thrown on a coldkey swap. - SwapError, + ColdkeySwapError, /// Insufficient Balance to Schedule coldkey swap InsufficientBalanceToPerformColdkeySwap, } diff --git a/pallets/subtensor/src/events.rs b/pallets/subtensor/src/events.rs index 913072090..2ed0d621d 100644 --- a/pallets/subtensor/src/events.rs +++ b/pallets/subtensor/src/events.rs @@ -159,5 +159,10 @@ mod events { /// The arbitration block for the coldkey swap arbitration_block: u64, }, + /// The arbitration period has been extended + ArbitrationPeriodExtended { + /// The account ID of the coldkey + coldkey: T::AccountId, + }, } } diff --git a/pallets/subtensor/src/swap.rs b/pallets/subtensor/src/swap.rs index 7bfcfb9e3..1c6673969 100644 --- a/pallets/subtensor/src/swap.rs +++ b/pallets/subtensor/src/swap.rs @@ -144,7 +144,7 @@ impl Pallet { // Actually do the swap. weight = weight.saturating_add( Self::perform_swap_coldkey(old_coldkey, new_coldkey) - .map_err(|_| Error::::SwapError)?, + .map_err(|_| Error::::ColdkeySwapError)?, ); Self::set_last_tx_block(new_coldkey, Self::get_current_block_as_u64()); @@ -335,12 +335,14 @@ impl Pallet { ColdkeySwapDestinations::::remove(&coldkey_i); weight = weight.saturating_add(T::DbWeight::get().reads_writes(1, 1)); - // If the wallets to swap is > 1 we do nothing. + // If the wallets to swap is > 1 we bump the arbitration period. if destinations_coldkeys.len() > 1 { - // Update the arbitration period but we still have the same wallet to swap to. - let next_arbitrage_period: u64 = - current_block.saturating_add(ArbitrationPeriod::::get()); - ColdkeyArbitrationBlock::::insert(coldkey_i.clone(), next_arbitrage_period); + // Set the arbitration period to u64::MAX until we have a senate vote + ColdkeyArbitrationBlock::::insert(coldkey_i.clone(), u64::MAX); + + Self::deposit_event(Event::ArbitrationPeriodExtended { + coldkey: coldkey_i.clone(), + }); } else if let Some(new_coldkey) = destinations_coldkeys.first() { // ONLY 1 wallet: Get the wallet to swap to. // Perform the swap. diff --git a/pallets/subtensor/tests/staking.rs b/pallets/subtensor/tests/staking.rs index 4c543f586..21137d77d 100644 --- a/pallets/subtensor/tests/staking.rs +++ b/pallets/subtensor/tests/staking.rs @@ -3547,14 +3547,14 @@ fn test_arbitrated_coldkey_swap_multiple_arbitrations() { // Arbitrate next block. assert_eq!( pallet_subtensor::ColdkeyArbitrationBlock::::get(coldkey), - SubtensorModule::get_current_block_as_u64() + 1 + u64::MAX ); // Arbitrate. step_block(1); - // Key is not in arbitration - assert!(!SubtensorModule::coldkey_in_arbitration(&coldkey)); + // Key is in arbitration + assert!(SubtensorModule::coldkey_in_arbitration(&coldkey)); // Owner schedules a swap for themselves lets go back into arbitration. let current_block = SubtensorModule::get_current_block_as_u64(); From 2e2b51d9fb9fd9fdd02ef38cbe1211bda1b7cc87 Mon Sep 17 00:00:00 2001 From: Samuel Dare Date: Mon, 8 Jul 2024 19:49:58 +0400 Subject: [PATCH 27/51] fix: broken test --- pallets/subtensor/tests/swap.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pallets/subtensor/tests/swap.rs b/pallets/subtensor/tests/swap.rs index 80f46292b..9db98e716 100644 --- a/pallets/subtensor/tests/swap.rs +++ b/pallets/subtensor/tests/swap.rs @@ -625,7 +625,7 @@ fn test_swap_stake_weight_update() { SubtensorModule::swap_stake(&old_hotkey, &new_hotkey, &mut weight); // Verify the weight update - let expected_weight = ::DbWeight::get().writes(3); + let expected_weight = ::DbWeight::get().writes(4); assert_eq!(weight, expected_weight); }); } @@ -1059,7 +1059,7 @@ fn test_do_swap_coldkey_success() { let netuid = 1u16; let stake_amount1 = 1000u64; let stake_amount2 = 2000u64; - let free_balance_old = 12345u64; + let free_balance_old = 12345u64 + MIN_BALANCE_TO_PERFORM_COLDKEY_SWAP; // Setup initial state add_network(netuid, 13, 0); From 82be091111b39f62e8d8abbd1d5851e529dedf2b Mon Sep 17 00:00:00 2001 From: Liam Date: Mon, 8 Jul 2024 17:51:48 +0200 Subject: [PATCH 28/51] use on_idle for swap_coldkeys logic --- pallets/subtensor/src/lib.rs | 19 +++++++++++-------- pallets/subtensor/src/swap.rs | 31 +++++++++++++++++++------------ 2 files changed, 30 insertions(+), 20 deletions(-) diff --git a/pallets/subtensor/src/lib.rs b/pallets/subtensor/src/lib.rs index 9ac7cc2eb..4c0efd78f 100644 --- a/pallets/subtensor/src/lib.rs +++ b/pallets/subtensor/src/lib.rs @@ -1328,18 +1328,23 @@ pub mod pallet { #[pallet::hooks] impl Hooks> for Pallet { + fn on_idle(_n: BlockNumberFor, remaining_weight: Weight) -> Weight { + // Unstake and transfer pending coldkeys up to the remaining weight + match Self::swap_coldkeys_this_block(&remaining_weight) { + Ok(weight_used) => weight_used, + Err(e) => { + log::error!("Error while swapping coldkeys: {:?}", e); + Weight::default() + } + } + } + // ---- Called on the initialization of this pallet. (the order of on_finalize calls is determined in the runtime) // // # Args: // * 'n': (BlockNumberFor): // - The number of the block we are initializing. fn on_initialize(_block_number: BlockNumberFor) -> Weight { - // Unstake all and transfer pending coldkeys - let swap_weight = match Self::swap_coldkeys_this_block() { - Ok(weight) => weight, - Err(_) => Weight::from_parts(0, 0), - }; - let block_step_result = Self::block_step(); match block_step_result { Ok(_) => { @@ -1348,7 +1353,6 @@ pub mod pallet { Weight::from_parts(110_634_229_000_u64, 0) .saturating_add(T::DbWeight::get().reads(8304_u64)) .saturating_add(T::DbWeight::get().writes(110_u64)) - .saturating_add(swap_weight) } Err(e) => { // --- If the block step was unsuccessful, return the weight anyway. @@ -1356,7 +1360,6 @@ pub mod pallet { Weight::from_parts(110_634_229_000_u64, 0) .saturating_add(T::DbWeight::get().reads(8304_u64)) .saturating_add(T::DbWeight::get().writes(110_u64)) - .saturating_add(swap_weight) } } } diff --git a/pallets/subtensor/src/swap.rs b/pallets/subtensor/src/swap.rs index 7bfcfb9e3..d6cea6cde 100644 --- a/pallets/subtensor/src/swap.rs +++ b/pallets/subtensor/src/swap.rs @@ -314,9 +314,9 @@ impl Pallet { /// /// # Returns /// - /// * `Weight` - The total weight consumed by the operation. - pub fn swap_coldkeys_this_block() -> Result { - let mut weight = frame_support::weights::Weight::from_parts(0, 0); + /// * `Weight` - The total weight consumed by this operation + pub fn swap_coldkeys_this_block(weight_limit: &Weight) -> Result { + let mut weight_used = frame_support::weights::Weight::from_parts(0, 0); // Get the block number let current_block: u64 = Self::get_current_block_as_u64(); @@ -325,15 +325,24 @@ impl Pallet { // Get the coldkeys to swap here and then remove them. let source_coldkeys: Vec = ColdkeysToSwapAtBlock::::get(current_block); ColdkeysToSwapAtBlock::::remove(current_block); - weight = weight.saturating_add(T::DbWeight::get().reads_writes(1, 1)); + weight_used = weight_used.saturating_add(T::DbWeight::get().reads_writes(1, 1)); // Iterate over all keys in swap and call perform_swap_coldkey for each + let mut keys_swapped = 0u64; for coldkey_i in source_coldkeys.iter() { + // Terminate early if we've exhausted the weight limit + // + // We care only about ref_time and not proof_size because we are a solochain. + if weight_used.ref_time() > weight_limit.ref_time() { + log::warn!("Could not finish swapping all coldkeys this block due to weight limit, breaking after swapping {} keys.", keys_swapped); + break; + } + // Get the wallets to swap to for this coldkey. let destinations_coldkeys: Vec = ColdkeySwapDestinations::::get(coldkey_i); ColdkeySwapDestinations::::remove(&coldkey_i); - weight = weight.saturating_add(T::DbWeight::get().reads_writes(1, 1)); + weight_used = weight_used.saturating_add(T::DbWeight::get().reads_writes(1, 1)); // If the wallets to swap is > 1 we do nothing. if destinations_coldkeys.len() > 1 { @@ -344,16 +353,14 @@ impl Pallet { } else if let Some(new_coldkey) = destinations_coldkeys.first() { // ONLY 1 wallet: Get the wallet to swap to. // Perform the swap. - if Self::perform_swap_coldkey(coldkey_i, new_coldkey) - .map(|w| weight = weight.saturating_add(w)) - .is_err() - { - return Err("Failed to perform coldkey swap"); - } + Self::perform_swap_coldkey(coldkey_i, new_coldkey).map(|weight| { + weight_used = weight_used.saturating_add(weight); + keys_swapped = keys_swapped.saturating_add(1); + })?; } } - Ok(weight) + Ok(weight_used) } pub fn perform_swap_coldkey( From 247c8615eee059f9bd2474ebae40cb1c4336c6a9 Mon Sep 17 00:00:00 2001 From: Greg Zaitsev Date: Mon, 8 Jul 2024 12:36:18 -0400 Subject: [PATCH 29/51] Increase base difficulty to 10M for PoW swaps --- pallets/subtensor/src/swap.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pallets/subtensor/src/swap.rs b/pallets/subtensor/src/swap.rs index 1c6673969..4c9b3a6d7 100644 --- a/pallets/subtensor/src/swap.rs +++ b/pallets/subtensor/src/swap.rs @@ -302,7 +302,7 @@ impl Pallet { /// Calculate the proof of work difficulty based on the number of swap attempts #[allow(clippy::arithmetic_side_effects)] fn calculate_pow_difficulty(swap_attempts: u32) -> U256 { - let base_difficulty: U256 = U256::from(1_000_000); // Base difficulty + let base_difficulty: U256 = U256::from(10_000_000); // Base difficulty base_difficulty * U256::from(2).pow(U256::from(swap_attempts)) } From c6dab93a0aaf560a4750b67156069a46dfb72c28 Mon Sep 17 00:00:00 2001 From: open-junius Date: Tue, 9 Jul 2024 01:02:56 +0800 Subject: [PATCH 30/51] add the unit test back --- pallets/subtensor/tests/swap.rs | 42 ++++++++++++++++----------------- 1 file changed, 21 insertions(+), 21 deletions(-) diff --git a/pallets/subtensor/tests/swap.rs b/pallets/subtensor/tests/swap.rs index 9db98e716..11164c97e 100644 --- a/pallets/subtensor/tests/swap.rs +++ b/pallets/subtensor/tests/swap.rs @@ -1387,24 +1387,24 @@ fn test_coldkey_has_associated_hotkeys() { }); } -// #[test] -// fn test_coldkey_arbitrated_sw() { -// new_test_ext(1).execute_with(|| { -// let coldkey = U256::from(1); -// let hotkey = U256::from(2); -// let netuid = 1u16; - -// // Setup initial state -// add_network(netuid, 13, 0); -// register_ok_neuron(netuid, hotkey, coldkey, 0); - -// // Check if coldkey has associated hotkeys -// assert!(SubtensorModule::coldkey_has_associated_hotkeys(&coldkey)); - -// // Check for a coldkey without associated hotkeys -// let unassociated_coldkey = U256::from(3); -// assert!(!SubtensorModule::coldkey_has_associated_hotkeys( -// &unassociated_coldkey -// )); -// }); -// } +#[test] +fn test_coldkey_arbitrated_sw() { + new_test_ext(1).execute_with(|| { + let coldkey = U256::from(1); + let hotkey = U256::from(2); + let netuid = 1u16; + + // Setup initial state + add_network(netuid, 13, 0); + register_ok_neuron(netuid, hotkey, coldkey, 0); + + // Check if coldkey has associated hotkeys + assert!(SubtensorModule::coldkey_has_associated_hotkeys(&coldkey)); + + // Check for a coldkey without associated hotkeys + let unassociated_coldkey = U256::from(3); + assert!(!SubtensorModule::coldkey_has_associated_hotkeys( + &unassociated_coldkey + )); + }); +} From e271f37b1d841163f5277e0c0b4dea97e831a250 Mon Sep 17 00:00:00 2001 From: Samuel Dare Date: Mon, 8 Jul 2024 21:09:18 +0400 Subject: [PATCH 31/51] fix broken tests --- pallets/subtensor/src/swap.rs | 116 ++++++++++++++------- pallets/subtensor/tests/swap.rs | 174 +++++++++++++++----------------- 2 files changed, 165 insertions(+), 125 deletions(-) diff --git a/pallets/subtensor/src/swap.rs b/pallets/subtensor/src/swap.rs index 5ab0ad9d2..11f76768c 100644 --- a/pallets/subtensor/src/swap.rs +++ b/pallets/subtensor/src/swap.rs @@ -767,7 +767,7 @@ impl Pallet { weight.saturating_accrue(T::DbWeight::get().reads_writes(1, 2)); } - /// Swaps all stakes associated with a coldkey from the old coldkey to the new coldkey. + /// Swaps the stake associated with a coldkey from the old coldkey to the new coldkey. /// /// # Arguments /// @@ -777,50 +777,96 @@ impl Pallet { /// /// # Effects /// - /// * Removes all stakes associated with the old coldkey. - /// * Inserts all stakes for the new coldkey. + /// * Transfers all stakes from the old coldkey to the new coldkey. + /// * Updates the ownership of hotkeys. + /// * Updates the total stake for both old and new coldkeys. /// * Updates the transaction weight. + /// + pub fn swap_stake_for_coldkey( old_coldkey: &T::AccountId, new_coldkey: &T::AccountId, weight: &mut Weight, ) { - // Swap the owners. - let old_owned_hotkeys = OwnedHotkeys::::get(old_coldkey); - for owned_key in old_owned_hotkeys.clone().iter() { - Owner::::insert(owned_key, new_coldkey); - // Find all hotkeys for this coldkey - let hotkeys = OwnedHotkeys::::get(old_coldkey); - weight.saturating_accrue(T::DbWeight::get().reads_writes(1, 0)); - for hotkey in hotkeys.iter() { - let stake = Stake::::get(&hotkey, old_coldkey); - Stake::::remove(&hotkey, old_coldkey); - Stake::::insert(&hotkey, new_coldkey, stake); - - // Update StakingHotkeys map - let staking_hotkeys = StakingHotkeys::::get(old_coldkey); - StakingHotkeys::::insert(new_coldkey.clone(), staking_hotkeys); - - weight.saturating_accrue(T::DbWeight::get().reads_writes(2, 3)); - } - OwnedHotkeys::::remove(old_coldkey.clone()); - OwnedHotkeys::::insert(new_coldkey.clone(), old_owned_hotkeys.clone()); - - // Swap all the keys the coldkey is staking too. - let staking_hotkeys = StakingHotkeys::::get(old_coldkey); - StakingHotkeys::::remove(old_coldkey.clone()); - for hotkey in staking_hotkeys.iter() { - // Remove the previous stake and re-insert it. - let stake = Stake::::get(hotkey, old_coldkey); - Stake::::remove(hotkey, old_coldkey); + // Retrieve the list of hotkeys owned by the old coldkey + let old_owned_hotkeys: Vec = OwnedHotkeys::::get(old_coldkey); + + // Initialize the total transferred stake to zero + let mut total_transferred_stake: u64 = 0u64; + + // Log the total stake of old and new coldkeys before the swap + log::info!( + "Before swap - Old coldkey total stake: {}", + TotalColdkeyStake::::get(old_coldkey) + ); + log::info!( + "Before swap - New coldkey total stake: {}", + TotalColdkeyStake::::get(new_coldkey) + ); + + // Iterate over each hotkey owned by the old coldkey + for hotkey in old_owned_hotkeys.iter() { + // Retrieve and remove the stake associated with the hotkey and old coldkey + let stake: u64 = Stake::::take(hotkey, old_coldkey); + log::info!("Transferring stake for hotkey {:?}: {}", hotkey, stake); + if stake > 0 { + // Insert the stake for the hotkey and new coldkey Stake::::insert(hotkey, new_coldkey, stake); - weight.saturating_accrue(T::DbWeight::get().reads_writes(2, 3)); + total_transferred_stake = total_transferred_stake.saturating_add(stake); + + // Update the owner of the hotkey to the new coldkey + Owner::::insert(hotkey, new_coldkey); + + // Update the transaction weight + *weight += T::DbWeight::get().reads_writes(2, 2); } - // Add the new staking keys value. - StakingHotkeys::::insert(new_coldkey.clone(), staking_hotkeys.clone()); } - } + // Log the total transferred stake + log::info!("Total transferred stake: {}", total_transferred_stake); + + // Update the total stake for both old and new coldkeys if any stake was transferred + if total_transferred_stake > 0 { + let old_coldkey_stake: u64 = TotalColdkeyStake::::get(old_coldkey); + let new_coldkey_stake: u64 = TotalColdkeyStake::::get(new_coldkey); + + TotalColdkeyStake::::insert(old_coldkey, 0); + TotalColdkeyStake::::insert( + new_coldkey, + new_coldkey_stake.saturating_add(old_coldkey_stake), + ); + + log::info!("Updated old coldkey stake from {} to 0", old_coldkey_stake); + log::info!( + "Updated new coldkey stake from {} to {}", + new_coldkey_stake, + new_coldkey_stake.saturating_add(old_coldkey_stake) + ); + + // Update the transaction weight + *weight += T::DbWeight::get().reads_writes(2, 2); + } + + // Update the list of owned hotkeys for both old and new coldkeys + OwnedHotkeys::::remove(old_coldkey); + OwnedHotkeys::::insert(new_coldkey, old_owned_hotkeys); + *weight += T::DbWeight::get().reads_writes(1, 2); + + // Update the staking hotkeys for both old and new coldkeys + let staking_hotkeys: Vec = StakingHotkeys::::take(old_coldkey); + StakingHotkeys::::insert(new_coldkey, staking_hotkeys); + *weight += T::DbWeight::get().reads_writes(1, 1); + + // Log the total stake of old and new coldkeys after the swap + log::info!( + "After swap - Old coldkey total stake: {}", + TotalColdkeyStake::::get(old_coldkey) + ); + log::info!( + "After swap - New coldkey total stake: {}", + TotalColdkeyStake::::get(new_coldkey) + ); + } /// Swaps the total hotkey-coldkey stakes for the current interval from the old coldkey to the new coldkey. /// /// # Arguments diff --git a/pallets/subtensor/tests/swap.rs b/pallets/subtensor/tests/swap.rs index 11164c97e..c9286d771 100644 --- a/pallets/subtensor/tests/swap.rs +++ b/pallets/subtensor/tests/swap.rs @@ -1072,6 +1072,20 @@ fn test_do_swap_coldkey_success() { stake_amount1 + stake_amount2 + free_balance_old, ); + // Log initial state + log::info!( + "Initial total stake: {}", + SubtensorModule::get_total_stake() + ); + log::info!( + "Initial old coldkey stake: {}", + SubtensorModule::get_total_stake_for_coldkey(&old_coldkey) + ); + log::info!( + "Initial new coldkey stake: {}", + SubtensorModule::get_total_stake_for_coldkey(&new_coldkey) + ); + // Add stake to the neurons assert_ok!(SubtensorModule::add_stake( <::RuntimeOrigin>::signed(old_coldkey), @@ -1084,22 +1098,23 @@ fn test_do_swap_coldkey_success() { stake_amount2 )); - // Verify initial stakes and balances - assert_eq!( - TotalColdkeyStake::::get(old_coldkey), - stake_amount1 + stake_amount2 + // Log state after adding stake + log::info!( + "Total stake after adding: {}", + SubtensorModule::get_total_stake() ); - assert_eq!(Stake::::get(hotkey1, old_coldkey), stake_amount1); - assert_eq!(Stake::::get(hotkey2, old_coldkey), stake_amount2); - assert_eq!( - OwnedHotkeys::::get(old_coldkey), - vec![hotkey1, hotkey2] + log::info!( + "Old coldkey stake after adding: {}", + SubtensorModule::get_total_stake_for_coldkey(&old_coldkey) ); - assert_eq!( - SubtensorModule::get_coldkey_balance(&old_coldkey), - free_balance_old + log::info!( + "New coldkey stake after adding: {}", + SubtensorModule::get_total_stake_for_coldkey(&new_coldkey) ); + // Record total stake before swap + let total_stake_before_swap = SubtensorModule::get_total_stake(); + // Perform the swap assert_ok!(SubtensorModule::do_swap_coldkey( <::RuntimeOrigin>::signed(old_coldkey), @@ -1107,6 +1122,20 @@ fn test_do_swap_coldkey_success() { &new_coldkey )); + // Log state after swap + log::info!( + "Total stake after swap: {}", + SubtensorModule::get_total_stake() + ); + log::info!( + "Old coldkey stake after swap: {}", + SubtensorModule::get_total_stake_for_coldkey(&old_coldkey) + ); + log::info!( + "New coldkey stake after swap: {}", + SubtensorModule::get_total_stake_for_coldkey(&new_coldkey) + ); + // Verify the swap assert_eq!(Owner::::get(hotkey1), new_coldkey); assert_eq!(Owner::::get(hotkey2), new_coldkey); @@ -1114,7 +1143,7 @@ fn test_do_swap_coldkey_success() { TotalColdkeyStake::::get(new_coldkey), stake_amount1 + stake_amount2 ); - assert!(!TotalColdkeyStake::::contains_key(old_coldkey)); + assert_eq!(TotalColdkeyStake::::get(old_coldkey), 0); assert_eq!(Stake::::get(hotkey1, new_coldkey), stake_amount1); assert_eq!(Stake::::get(hotkey2, new_coldkey), stake_amount2); assert!(!Stake::::contains_key(hotkey1, old_coldkey)); @@ -1134,6 +1163,13 @@ fn test_do_swap_coldkey_success() { ); assert_eq!(SubtensorModule::get_coldkey_balance(&old_coldkey), 0); + // Verify total stake remains unchanged + assert_eq!( + SubtensorModule::get_total_stake(), + total_stake_before_swap, + "Total stake changed unexpectedly" + ); + // Verify event emission System::assert_last_event( Event::ColdkeySwapped { @@ -1145,31 +1181,6 @@ fn test_do_swap_coldkey_success() { }); } -#[test] -fn test_swap_total_coldkey_stake() { - new_test_ext(1).execute_with(|| { - let old_coldkey = U256::from(1); - let new_coldkey = U256::from(2); - let stake_amount = 1000u64; - let mut weight = Weight::zero(); - - // Initialize TotalColdkeyStake for old_coldkey - TotalColdkeyStake::::insert(old_coldkey, stake_amount); - - // Perform the swap - SubtensorModule::swap_total_coldkey_stake(&old_coldkey, &new_coldkey, &mut weight); - - // Verify the swap - assert_eq!(TotalColdkeyStake::::get(new_coldkey), stake_amount); - assert!(!TotalColdkeyStake::::contains_key(old_coldkey)); - - // Verify weight update - let expected_weight = ::DbWeight::get().reads_writes(1, 2); - assert_eq!(weight, expected_weight); - }); -} - -// SKIP_WASM_BUILD=1 RUST_LOG=info cargo test --test swap -- test_swap_stake_for_coldkey --exact --nocapture #[test] fn test_swap_stake_for_coldkey() { new_test_ext(1).execute_with(|| { @@ -1179,77 +1190,60 @@ fn test_swap_stake_for_coldkey() { let hotkey2 = U256::from(4); let stake_amount1 = 1000u64; let stake_amount2 = 2000u64; + let total_stake = stake_amount1 + stake_amount2; let mut weight = Weight::zero(); - SubtensorModule::add_balance_to_coldkey_account( - &old_coldkey, - stake_amount1 + stake_amount2 + 123456, - ); - add_network(1, 13, 0); - register_ok_neuron(1, hotkey1, old_coldkey, 0); - register_ok_neuron(1, hotkey2, old_coldkey, 0); - assert_ok!(SubtensorModule::add_stake( - <::RuntimeOrigin>::signed(old_coldkey), - hotkey1, - stake_amount1 - )); - assert_ok!(SubtensorModule::add_stake( - <::RuntimeOrigin>::signed(old_coldkey), - hotkey2, - stake_amount2 - )); - // Get owned keys - assert_eq!( - SubtensorModule::get_owned_hotkeys(&old_coldkey), - vec![hotkey1, hotkey2] - ); - assert_eq!(SubtensorModule::get_owned_hotkeys(&new_coldkey), vec![]); - // Get all staked keys. - assert_eq!( - SubtensorModule::get_all_staked_hotkeys(&old_coldkey), - vec![hotkey1, hotkey2] - ); - assert_eq!( - SubtensorModule::get_all_staked_hotkeys(&new_coldkey), - vec![] - ); + // Setup initial state + OwnedHotkeys::::insert(old_coldkey, vec![hotkey1, hotkey2]); + Stake::::insert(hotkey1, old_coldkey, stake_amount1); + Stake::::insert(hotkey2, old_coldkey, stake_amount2); + TotalHotkeyStake::::insert(hotkey1, stake_amount1); + TotalHotkeyStake::::insert(hotkey2, stake_amount2); + TotalColdkeyStake::::insert(old_coldkey, total_stake); + + // Set up total issuance + TotalIssuance::::put(total_stake); + TotalStake::::put(total_stake); + + // Record initial values + let initial_total_issuance = SubtensorModule::get_total_issuance(); + let initial_total_stake = SubtensorModule::get_total_stake(); // Perform the swap SubtensorModule::swap_stake_for_coldkey(&old_coldkey, &new_coldkey, &mut weight); - // Get owned keys + // Verify ownership transfer assert_eq!( SubtensorModule::get_owned_hotkeys(&new_coldkey), vec![hotkey1, hotkey2] ); assert_eq!(SubtensorModule::get_owned_hotkeys(&old_coldkey), vec![]); - // Get all staked keys. - assert_eq!( - SubtensorModule::get_all_staked_hotkeys(&new_coldkey), - vec![hotkey1, hotkey2] - ); - assert_eq!( - SubtensorModule::get_all_staked_hotkeys(&old_coldkey), - vec![] - ); - // Verify the swap + // Verify stake transfer assert_eq!(Stake::::get(hotkey1, new_coldkey), stake_amount1); assert_eq!(Stake::::get(hotkey2, new_coldkey), stake_amount2); - assert!(!Stake::::contains_key(hotkey1, old_coldkey)); - assert!(!Stake::::contains_key(hotkey2, old_coldkey)); + assert_eq!(Stake::::get(hotkey1, old_coldkey), 0); + assert_eq!(Stake::::get(hotkey2, old_coldkey), 0); + + // Verify TotalColdkeyStake + assert_eq!(TotalColdkeyStake::::get(new_coldkey), total_stake); + assert_eq!(TotalColdkeyStake::::get(old_coldkey), 0); // Verify TotalHotkeyStake remains unchanged assert_eq!(TotalHotkeyStake::::get(hotkey1), stake_amount1); assert_eq!(TotalHotkeyStake::::get(hotkey2), stake_amount2); - // Verify TotalStake and TotalIssuance remain unchanged - assert_eq!(TotalStake::::get(), stake_amount1 + stake_amount2); - assert_eq!(TotalIssuance::::get(), stake_amount1 + stake_amount2); - - // Verify weight update - let expected_weight = ::DbWeight::get().reads_writes(5, 6); - assert_eq!(weight, expected_weight); + // Verify total stake and issuance remain unchanged + assert_eq!( + SubtensorModule::get_total_stake(), + initial_total_stake, + "Total stake changed unexpectedly" + ); + assert_eq!( + SubtensorModule::get_total_issuance(), + initial_total_issuance, + "Total issuance changed unexpectedly" + ); }); } From 54a1466fd0b45e6c9b870beaf52ea6b2aca6fb73 Mon Sep 17 00:00:00 2001 From: Samuel Dare Date: Mon, 8 Jul 2024 22:15:33 +0400 Subject: [PATCH 32/51] chore: fix tests --- pallets/subtensor/src/swap.rs | 2 +- pallets/subtensor/tests/mock.rs | 14 +- pallets/subtensor/tests/staking.rs | 270 ++++++++++++++++------------- pallets/subtensor/tests/swap.rs | 42 ++--- 4 files changed, 177 insertions(+), 151 deletions(-) diff --git a/pallets/subtensor/src/swap.rs b/pallets/subtensor/src/swap.rs index 11f76768c..498894f87 100644 --- a/pallets/subtensor/src/swap.rs +++ b/pallets/subtensor/src/swap.rs @@ -301,7 +301,7 @@ impl Pallet { /// Calculate the proof of work difficulty based on the number of swap attempts #[allow(clippy::arithmetic_side_effects)] - fn calculate_pow_difficulty(swap_attempts: u32) -> U256 { + pub fn calculate_pow_difficulty(swap_attempts: u32) -> U256 { let base_difficulty: U256 = U256::from(10_000_000); // Base difficulty base_difficulty * U256::from(2).pow(U256::from(swap_attempts)) } diff --git a/pallets/subtensor/tests/mock.rs b/pallets/subtensor/tests/mock.rs index 3e7e74f95..9e21f2a63 100644 --- a/pallets/subtensor/tests/mock.rs +++ b/pallets/subtensor/tests/mock.rs @@ -2,21 +2,22 @@ use frame_support::derive_impl; use frame_support::dispatch::DispatchResultWithPostInfo; use frame_support::weights::constants::RocksDbWeight; +// use frame_support::weights::constants::WEIGHT_PER_SECOND; +use frame_support::weights::Weight; use frame_support::{ assert_ok, parameter_types, traits::{Everything, Hooks}, - weights, }; use frame_system as system; use frame_system::{limits, EnsureNever, EnsureRoot, RawOrigin}; +use pallet_collective::MemberCount; use sp_core::{Get, H256, U256}; +use sp_runtime::Perbill; use sp_runtime::{ traits::{BlakeTwo256, IdentityLookup}, BuildStorage, }; -use pallet_collective::MemberCount; - type Block = frame_system::mocking::MockBlock; // Configure a mock runtime to test the pallet. @@ -86,7 +87,7 @@ impl pallet_balances::Config for Test { #[derive_impl(frame_system::config_preludes::TestDefaultConfig)] impl system::Config for Test { type BaseCallFilter = Everything; - type BlockWeights = (); + type BlockWeights = BlockWeights; type BlockLength = (); type DbWeight = RocksDbWeight; type RuntimeOrigin = RuntimeOrigin; @@ -114,7 +115,10 @@ parameter_types! { pub const InitialMinAllowedWeights: u16 = 0; pub const InitialEmissionValue: u16 = 0; pub const InitialMaxWeightsLimit: u16 = u16::MAX; - pub BlockWeights: limits::BlockWeights = limits::BlockWeights::simple_max(weights::Weight::from_parts(1024, 0)); + pub BlockWeights: limits::BlockWeights = limits::BlockWeights::with_sensible_defaults( + Weight::from_parts(2_000_000_000_000, u64::MAX), + Perbill::from_percent(75), + ); pub const ExistentialDeposit: Balance = 1; pub const TransactionByteFee: Balance = 100; pub const SDebug:u64 = 1; diff --git a/pallets/subtensor/tests/staking.rs b/pallets/subtensor/tests/staking.rs index b8b51ff08..c6216b456 100644 --- a/pallets/subtensor/tests/staking.rs +++ b/pallets/subtensor/tests/staking.rs @@ -3,6 +3,8 @@ use frame_support::pallet_prelude::{ InvalidTransaction, TransactionValidity, TransactionValidityError, }; +use frame_support::traits::{OnFinalize, OnIdle, OnInitialize}; +use frame_support::weights::Weight; use frame_support::{assert_err, assert_noop, assert_ok, traits::Currency}; use frame_system::Config; mod mock; @@ -3165,7 +3167,8 @@ fn test_arbitrated_coldkey_swap_success() { let (current_coldkey, hotkey, new_coldkey) = setup_test_environment(); let current_block = SubtensorModule::get_current_block_as_u64(); - let (work, nonce) = generate_valid_pow(¤t_coldkey, current_block, 1_000_000); + let (work, nonce) = + generate_valid_pow(¤t_coldkey, current_block, U256::from(10_000_000u64)); SubtensorModule::add_balance_to_coldkey_account( ¤t_coldkey, MIN_BALANCE_TO_PERFORM_COLDKEY_SWAP, @@ -3173,7 +3176,7 @@ fn test_arbitrated_coldkey_swap_success() { assert_ok!(SubtensorModule::do_schedule_coldkey_swap( ¤t_coldkey.clone(), &new_coldkey, - work, + work.to_fixed_bytes().to_vec(), current_block, nonce )); @@ -3236,13 +3239,14 @@ fn test_arbitrated_coldkey_swap_same_coldkey() { let (current_coldkey, _hotkey, _) = setup_test_environment(); let current_block = SubtensorModule::get_current_block_as_u64(); - let (work, nonce) = generate_valid_pow(¤t_coldkey, current_block, 1_000_000); + let (work, nonce) = + generate_valid_pow(¤t_coldkey, current_block, U256::from(10_000_000u64)); assert_noop!( SubtensorModule::do_schedule_coldkey_swap( ¤t_coldkey.clone(), ¤t_coldkey, - work, + work.to_fixed_bytes().to_vec(), current_block, nonce ), @@ -3286,13 +3290,14 @@ fn test_arbitrated_coldkey_swap_no_balance() { // Generate valid PoW let current_block = SubtensorModule::get_current_block_as_u64(); - let (work, nonce) = generate_valid_pow(¤t_coldkey, current_block, 1_000_000); + let (work, nonce) = + generate_valid_pow(¤t_coldkey, current_block, U256::from(10_000_000u64)); // Try to schedule coldkey swap let result = SubtensorModule::do_schedule_coldkey_swap( ¤t_coldkey.clone(), &new_coldkey, - work, + work.to_fixed_bytes().to_vec(), current_block, nonce, ); @@ -3368,20 +3373,24 @@ fn test_arbitrated_coldkey_swap_with_no_stake() { assert_eq!(Balances::total_balance(&new_coldkey), 0); let current_block = SubtensorModule::get_current_block_as_u64(); - let (work, nonce) = generate_valid_pow(¤t_coldkey, current_block, 1_000_000); + let (work, nonce) = + generate_valid_pow(¤t_coldkey, current_block, U256::from(10_000_000u64)); - // Perform unstake and transfer + // Schedule coldkey swap assert_ok!(SubtensorModule::do_schedule_coldkey_swap( ¤t_coldkey.clone(), &new_coldkey, - work, + work.to_fixed_bytes().to_vec(), current_block, nonce )); - // Make 7200 * 4 blocks pass + // Make 7200 * 4 blocks pass, simulating on_idle for each block let drain_block: u64 = 7200 * 3 + 1; - run_to_block(drain_block); + for _ in 0..drain_block { + next_block(); + SubtensorModule::on_idle(System::block_number(), Weight::MAX); + } // Print final balances log::info!( @@ -3426,19 +3435,23 @@ fn test_arbitrated_coldkey_swap_with_multiple_stakes() { )); let current_block = SubtensorModule::get_current_block_as_u64(); - let (work, nonce) = generate_valid_pow(¤t_coldkey, current_block, 1_000_000); + let (work, nonce) = + generate_valid_pow(¤t_coldkey, current_block, U256::from(10_000_000u64)); assert_ok!(SubtensorModule::do_schedule_coldkey_swap( ¤t_coldkey.clone(), &new_coldkey, - work, + work.to_fixed_bytes().to_vec(), current_block, nonce )); - // Make 7200 * 4 blocks pass + // Make 7200 * 4 blocks pass, simulating on_idle for each block let drain_block: u64 = 7200 * 3 + 1; - run_to_block(drain_block); + for _ in 0..drain_block { + next_block(); + SubtensorModule::on_idle(System::block_number(), Weight::MAX); + } // Check that all stake has been removed assert_eq!(SubtensorModule::get_total_stake_for_hotkey(&hotkey), 800); @@ -3459,16 +3472,16 @@ fn test_arbitrated_coldkey_swap_with_multiple_stakes() { assert_eq!(SubtensorModule::get_coldkey_balance(¤t_coldkey), 0); }); } - // SKIP_WASM_BUILD=1 RUST_LOG=info cargo test --test staking -- test_arbitrated_coldkey_swap_multiple_arbitrations --exact --nocapture #[test] fn test_arbitrated_coldkey_swap_multiple_arbitrations() { new_test_ext(1).execute_with(|| { - // Create coldkey with two choices. + // Create coldkey with three choices. let coldkey: AccountId = U256::from(1); let new_coldkey1: AccountId = U256::from(2); let new_coldkey2: AccountId = U256::from(3); - let hotkey: AccountId = U256::from(4); + let new_coldkey3: AccountId = U256::from(4); + let hotkey: AccountId = U256::from(5); // Setup network state. add_network(1, 0, 0); @@ -3476,116 +3489,94 @@ fn test_arbitrated_coldkey_swap_multiple_arbitrations() { &coldkey, MIN_BALANCE_TO_PERFORM_COLDKEY_SWAP, ); - ArbitrationPeriod::::put(1); // One block arbitration period. + ArbitrationPeriod::::put(5); // Set arbitration period to 5 blocks register_ok_neuron(1, hotkey, coldkey, 0); let current_block = SubtensorModule::get_current_block_as_u64(); - let (work1, nonce1) = generate_valid_pow(&coldkey, current_block, 1_000_000); - let (work2, nonce2) = generate_valid_pow(&coldkey, current_block, 2_000_000); - - // Owner schedules a swap for themselves. + let (work1, nonce1) = + generate_valid_pow(&coldkey, current_block, U256::from(10_000_000u64)); + let (work2, nonce2) = + generate_valid_pow(&coldkey, current_block, U256::from(20_000_000u64)); + let (work3, nonce3) = + generate_valid_pow(&coldkey, current_block, U256::from(30_000_000u64)); + + // Schedule three swaps assert_ok!(SubtensorModule::do_schedule_coldkey_swap( &coldkey.clone(), &new_coldkey1, - work1, + work1.to_fixed_bytes().to_vec(), current_block, nonce1 )); - - // Attacker schedules the second swap for themselves. assert_ok!(SubtensorModule::do_schedule_coldkey_swap( &coldkey.clone(), &new_coldkey2, - work2, + work2.to_fixed_bytes().to_vec(), current_block, nonce2 )); + assert_ok!(SubtensorModule::do_schedule_coldkey_swap( + &coldkey.clone(), + &new_coldkey3, + work3.to_fixed_bytes().to_vec(), + current_block, + nonce3 + )); - // Both keys are added in swap destinations. + // All three keys are added in swap destinations. assert_eq!( pallet_subtensor::ColdkeySwapDestinations::::get(coldkey), - vec![new_coldkey1, new_coldkey2] + vec![new_coldkey1, new_coldkey2, new_coldkey3] ); - // Check that we are arbitrating next block. - assert_eq!( - pallet_subtensor::ColdkeysToSwapAtBlock::::get( - SubtensorModule::get_current_block_as_u64() + 1 - ), - vec![coldkey] - ); + // Simulate the passage of blocks and on_idle calls + for i in 0..(7200 * 3 + 1) { + // Simulate 10 blocks + next_block(); + SubtensorModule::on_idle(System::block_number(), Weight::MAX); - // Key is in arbitration. - assert!(SubtensorModule::coldkey_in_arbitration(&coldkey)); + log::info!( + "Block {}: Coldkey in arbitration: {}, Swap destinations: {:?}", + i + 1, + SubtensorModule::coldkey_in_arbitration(&coldkey), + pallet_subtensor::ColdkeySwapDestinations::::get(coldkey) + ); + } - // Arbitrate next block. + // Check that the swap destinations remain unchanged due to multiple (>2) swap calls assert_eq!( - pallet_subtensor::ColdkeyArbitrationBlock::::get(coldkey), - SubtensorModule::get_current_block_as_u64() + 1 + pallet_subtensor::ColdkeySwapDestinations::::get(coldkey), + vec![new_coldkey1, new_coldkey2, new_coldkey3], + "ColdkeySwapDestinations should remain unchanged with more than two swap calls" ); - // Arbitrate. - step_block(1); - - // Both keys are removed and a new period begins. - assert_eq!( - pallet_subtensor::ColdkeySwapDestinations::::get(coldkey), - vec![] + // Key remains in arbitration due to multiple (>2) swap calls + assert!( + SubtensorModule::coldkey_in_arbitration(&coldkey), + "Coldkey should remain in arbitration with more than two swap calls" ); - // Arbitration has been pushed back but there are no keys to add to the list to arbitrate. + // Check that no balance has been transferred assert_eq!( - pallet_subtensor::ColdkeysToSwapAtBlock::::get( - SubtensorModule::get_current_block_as_u64() + 1 - ), - vec![] + SubtensorModule::get_coldkey_balance(&coldkey), + MIN_BALANCE_TO_PERFORM_COLDKEY_SWAP, + "Original coldkey balance should remain unchanged" ); - - // Key is in arbitration. - assert!(SubtensorModule::coldkey_in_arbitration(&coldkey)); - - // Arbitrate next block. assert_eq!( - pallet_subtensor::ColdkeyArbitrationBlock::::get(coldkey), - u64::MAX + SubtensorModule::get_coldkey_balance(&new_coldkey1), + 0, + "New coldkey1 should not receive any balance" ); - - // Arbitrate. - step_block(1); - - // Key is in arbitration - assert!(SubtensorModule::coldkey_in_arbitration(&coldkey)); - - // Owner schedules a swap for themselves lets go back into arbitration. - let current_block = SubtensorModule::get_current_block_as_u64(); - let (work3, nonce3) = generate_valid_pow(&coldkey, current_block, 4_000_000); - assert_ok!(SubtensorModule::do_schedule_coldkey_swap( - &coldkey.clone(), - &new_coldkey1, - work3, - current_block, - nonce3 - )); - - // Key goes back into arbitration. - assert!(SubtensorModule::coldkey_in_arbitration(&coldkey)); - - // Arbitrate next block. assert_eq!( - pallet_subtensor::ColdkeyArbitrationBlock::::get(coldkey), - SubtensorModule::get_current_block_as_u64() + 1 + SubtensorModule::get_coldkey_balance(&new_coldkey2), + 0, + "New coldkey2 should not receive any balance" ); - - // Arbitrate. - step_block(1); - - // New key gets amount the other keys are empty. - assert_eq!(SubtensorModule::get_coldkey_balance(&coldkey), 0); assert_eq!( - SubtensorModule::get_coldkey_balance(&new_coldkey1), - MIN_BALANCE_TO_PERFORM_COLDKEY_SWAP + SubtensorModule::get_coldkey_balance(&new_coldkey3), + 0, + "New coldkey3 should not receive any balance" ); - assert_eq!(SubtensorModule::get_coldkey_balance(&new_coldkey2), 0); }); } @@ -3595,54 +3586,63 @@ fn test_arbitrated_coldkey_swap_existing_destination() { new_test_ext(1).execute_with(|| { let (current_coldkey, _hotkey, new_coldkey) = setup_test_environment(); let another_coldkey = U256::from(4); + let third_coldkey = U256::from(5); let current_block = SubtensorModule::get_current_block_as_u64(); - let (work1, nonce1) = generate_valid_pow(¤t_coldkey, current_block, 1_000_000); - let (work2, nonce2) = generate_valid_pow(¤t_coldkey, current_block, 2_000_000); - let (work3, nonce3) = generate_valid_pow(¤t_coldkey, current_block, 4_000_000); SubtensorModule::add_balance_to_coldkey_account( ¤t_coldkey, MIN_BALANCE_TO_PERFORM_COLDKEY_SWAP, ); + // First swap attempt (0 existing destinations) + let difficulty1 = SubtensorModule::calculate_pow_difficulty(0); + let (work1, nonce1) = generate_valid_pow(¤t_coldkey, current_block, difficulty1); + // Schedule a swap to new_coldkey assert_ok!(SubtensorModule::do_schedule_coldkey_swap( ¤t_coldkey, &new_coldkey, - work1, + work1.to_fixed_bytes().to_vec(), current_block, nonce1 )); + // Second swap attempt (1 existing destination) + let difficulty2 = SubtensorModule::calculate_pow_difficulty(1); + let (work2, nonce2) = generate_valid_pow(¤t_coldkey, current_block, difficulty2); + // Attempt to schedule a swap to the same new_coldkey again assert_noop!( SubtensorModule::do_schedule_coldkey_swap( ¤t_coldkey.clone(), &new_coldkey, - work2.clone(), + work2.to_fixed_bytes().to_vec(), current_block, nonce2 ), Error::::DuplicateColdkey ); - // Schedule a swap to another_coldkey + // Schedule a swap to another_coldkey (still 1 existing destination) assert_ok!(SubtensorModule::do_schedule_coldkey_swap( ¤t_coldkey.clone(), &another_coldkey, - work2, + work2.to_fixed_bytes().to_vec(), current_block, nonce2 )); + // Third swap attempt (2 existing destinations) + let difficulty3 = SubtensorModule::calculate_pow_difficulty(2); + let (work3, nonce3) = generate_valid_pow(¤t_coldkey, current_block, difficulty3); + // Attempt to schedule a third swap - let third_coldkey = U256::from(5); assert_noop!( SubtensorModule::do_schedule_coldkey_swap( ¤t_coldkey.clone(), &third_coldkey, - work3, + work3.to_fixed_bytes().to_vec(), current_block, nonce3 ), @@ -3658,8 +3658,10 @@ fn test_arbitration_period_extension() { let another_coldkey = U256::from(4); let current_block = SubtensorModule::get_current_block_as_u64(); - let (work1, nonce1) = generate_valid_pow(¤t_coldkey, current_block, 1_000_000); - let (work2, nonce2) = generate_valid_pow(¤t_coldkey, current_block, 2_000_000); + let (work1, nonce1) = + generate_valid_pow(¤t_coldkey, current_block, U256::from(10_000_000u64)); + let (work2, nonce2) = + generate_valid_pow(¤t_coldkey, current_block, U256::from(20_000_000u64)); SubtensorModule::add_balance_to_coldkey_account( ¤t_coldkey, MIN_BALANCE_TO_PERFORM_COLDKEY_SWAP, @@ -3669,7 +3671,7 @@ fn test_arbitration_period_extension() { assert_ok!(SubtensorModule::do_schedule_coldkey_swap( ¤t_coldkey.clone(), &new_coldkey, - work1, + work1.to_fixed_bytes().to_vec(), current_block, nonce1 )); @@ -3678,7 +3680,7 @@ fn test_arbitration_period_extension() { assert_ok!(SubtensorModule::do_schedule_coldkey_swap( ¤t_coldkey.clone(), &another_coldkey, - work2, + work2.to_fixed_bytes().to_vec(), current_block, nonce2 )); @@ -3726,25 +3728,26 @@ fn test_concurrent_arbitrated_coldkey_swaps() { ); let current_block = SubtensorModule::get_current_block_as_u64(); - let (work1, nonce1) = generate_valid_pow(&coldkey1, current_block, 1_000_000); - let (work2, nonce2) = generate_valid_pow(&coldkey2, current_block, 1_000_000); - + let (work1, nonce1) = + generate_valid_pow(&coldkey1, current_block, U256::from(10_000_000u64)); + let (work2, nonce2) = + generate_valid_pow(&coldkey2, current_block, U256::from(10_000_000u64)); // Schedule swaps for both coldkeys assert_ok!(SubtensorModule::do_schedule_coldkey_swap( &coldkey1.clone(), &new_coldkey1, - work1, + work1.to_fixed_bytes().to_vec(), current_block, nonce1 )); assert_ok!(SubtensorModule::do_schedule_coldkey_swap( &coldkey2.clone(), &new_coldkey2, - work2, + work2.to_fixed_bytes().to_vec(), current_block, nonce2 )); - // Make 7200 * 4 blocks pass + // Make 7200 * 3 blocks pass let drain_block: u64 = 7200 * 3 + 1; run_to_block(drain_block); @@ -3770,7 +3773,11 @@ fn test_get_remaining_arbitration_period() { let new_coldkey_account_id = U256::from(54321); // arbitrary new coldkey let current_block = SubtensorModule::get_current_block_as_u64(); - let (work, nonce) = generate_valid_pow(&coldkey_account_id, current_block, 1_000_000); + let (work, nonce) = generate_valid_pow( + &coldkey_account_id, + current_block, + U256::from(10_000_000u64), + ); SubtensorModule::add_balance_to_coldkey_account( &coldkey_account_id, @@ -3781,7 +3788,7 @@ fn test_get_remaining_arbitration_period() { assert_ok!(SubtensorModule::do_schedule_coldkey_swap( &coldkey_account_id.clone(), &new_coldkey_account_id, - work, + work.to_fixed_bytes().to_vec(), current_block, nonce )); @@ -3826,13 +3833,17 @@ fn test_transfer_coldkey_in_arbitration() { ); let current_block = SubtensorModule::get_current_block_as_u64(); - let (work, nonce) = generate_valid_pow(&coldkey_account_id, current_block, 1_000_000); + let (work, nonce) = generate_valid_pow( + &coldkey_account_id, + current_block, + U256::from(10_000_000u64), + ); // Schedule a coldkey swap to put the coldkey in arbitration assert_ok!(SubtensorModule::do_schedule_coldkey_swap( &coldkey_account_id.clone(), &new_coldkey_account_id, - work, + work.to_fixed_bytes().to_vec(), current_block, nonce )); @@ -3868,13 +3879,14 @@ fn test_add_stake_coldkey_in_arbitration() { ); let current_block = SubtensorModule::get_current_block_as_u64(); - let (work, nonce) = generate_valid_pow(&coldkey_account_id, current_block, 1_000_000); + let (work, nonce) = + generate_valid_pow(&coldkey_account_id, current_block, U256::from(1_000_000u64)); // Schedule a coldkey swap to put the coldkey in arbitration assert_ok!(SubtensorModule::do_schedule_coldkey_swap( &coldkey_account_id.clone(), &new_coldkey_account_id, - work, + work.to_fixed_bytes().to_vec(), current_block, nonce )); @@ -3907,13 +3919,14 @@ fn test_remove_stake_coldkey_in_arbitration() { SubtensorModule::increase_stake_on_hotkey_account(&hotkey_account_id, 1000); let current_block = SubtensorModule::get_current_block_as_u64(); - let (work, nonce) = generate_valid_pow(&coldkey_account_id, current_block, 1_000_000); + let (work, nonce) = + generate_valid_pow(&coldkey_account_id, current_block, U256::from(1_000_000u64)); // Schedule a coldkey swap to put the coldkey in arbitration assert_ok!(SubtensorModule::do_schedule_coldkey_swap( &coldkey_account_id.clone(), &new_coldkey_account_id, - work, + work.to_fixed_bytes().to_vec(), current_block, nonce )); @@ -3951,13 +3964,22 @@ fn validate_transaction(who: &AccountId, call: &RuntimeCall) -> TransactionValid } // Helper function to generate valid PoW -fn generate_valid_pow(coldkey: &AccountId, block_number: u64, difficulty: u32) -> (Vec, u64) { +fn generate_valid_pow(coldkey: &U256, block_number: u64, difficulty: U256) -> (H256, u64) { let mut nonce: u64 = 0; loop { let work = SubtensorModule::create_seal_hash(block_number, nonce, coldkey); - if SubtensorModule::hash_meets_difficulty(&work, difficulty.into()) { - return (work.as_bytes().to_vec(), nonce); + if SubtensorModule::hash_meets_difficulty(&work, difficulty) { + return (work, nonce); } - nonce = nonce.saturating_add(1); + nonce += 1; } } + +// Helper function to advance to the next block and run hooks +fn next_block() { + let current_block = System::block_number(); + System::on_finalize(current_block); + System::set_block_number(current_block + 1); + System::on_initialize(System::block_number()); + SubtensorModule::on_initialize(System::block_number()); +} diff --git a/pallets/subtensor/tests/swap.rs b/pallets/subtensor/tests/swap.rs index c9286d771..d698e4385 100644 --- a/pallets/subtensor/tests/swap.rs +++ b/pallets/subtensor/tests/swap.rs @@ -1381,24 +1381,24 @@ fn test_coldkey_has_associated_hotkeys() { }); } -#[test] -fn test_coldkey_arbitrated_sw() { - new_test_ext(1).execute_with(|| { - let coldkey = U256::from(1); - let hotkey = U256::from(2); - let netuid = 1u16; - - // Setup initial state - add_network(netuid, 13, 0); - register_ok_neuron(netuid, hotkey, coldkey, 0); - - // Check if coldkey has associated hotkeys - assert!(SubtensorModule::coldkey_has_associated_hotkeys(&coldkey)); - - // Check for a coldkey without associated hotkeys - let unassociated_coldkey = U256::from(3); - assert!(!SubtensorModule::coldkey_has_associated_hotkeys( - &unassociated_coldkey - )); - }); -} +// #[test] +// fn test_coldkey_arbitrated_sw() { +// new_test_ext(1).execute_with(|| { +// let coldkey = U256::from(1); +// let hotkey = U256::from(2); +// let netuid = 1u16; + +// // Setup initial state +// add_network(netuid, 13, 0); +// register_ok_neuron(netuid, hotkey, coldkey, 0); + +// // Check if coldkey has associated hotkeys +// assert!(SubtensorModule::coldkey_has_associated_hotkeys(&coldkey)); + +// // Check for a coldkey without associated hotkeys +// let unassociated_coldkey = U256::from(3); +// assert!(!SubtensorModule::coldkey_has_associated_hotkeys( +// &unassociated_coldkey +// )); +// }); +// } From 7bf65241c189d037c5b19c8a2d13f2711a2a9e51 Mon Sep 17 00:00:00 2001 From: Samuel Dare Date: Mon, 8 Jul 2024 23:06:15 +0400 Subject: [PATCH 33/51] chore: fix test_arbitrated_coldkey_swap_multiple_arbitrations --- pallets/subtensor/src/errors.rs | 2 ++ pallets/subtensor/src/swap.rs | 37 ++++++++++++++------------------- 2 files changed, 18 insertions(+), 21 deletions(-) diff --git a/pallets/subtensor/src/errors.rs b/pallets/subtensor/src/errors.rs index 07a16b048..100be89b7 100644 --- a/pallets/subtensor/src/errors.rs +++ b/pallets/subtensor/src/errors.rs @@ -154,5 +154,7 @@ mod errors { ColdkeySwapError, /// Insufficient Balance to Schedule coldkey swap InsufficientBalanceToPerformColdkeySwap, + /// The maximum number of coldkey destinations has been reached + MaxColdkeyDestinationsReached, } } diff --git a/pallets/subtensor/src/swap.rs b/pallets/subtensor/src/swap.rs index 498894f87..d7a0335e2 100644 --- a/pallets/subtensor/src/swap.rs +++ b/pallets/subtensor/src/swap.rs @@ -265,12 +265,14 @@ impl Pallet { Error::::DuplicateColdkey ); - // If the destinations keys are empty or have size 1 then we will add the new coldkey to the list - if destination_coldkeys.is_empty() || destination_coldkeys.len() == 1_usize { + // If the destinations keys are empty or have less than the maximum allowed, we will add the new coldkey to the list + const MAX_COLDKEY_DESTINATIONS: usize = 10; + + if destination_coldkeys.len() < MAX_COLDKEY_DESTINATIONS { destination_coldkeys.push(new_coldkey.clone()); ColdkeySwapDestinations::::insert(old_coldkey.clone(), destination_coldkeys.clone()); } else { - return Err(Error::::ColdkeyIsInArbitration.into()); + return Err(Error::::MaxColdkeyDestinationsReached.into()); } // It is the first time we have seen this key @@ -318,43 +320,36 @@ impl Pallet { pub fn swap_coldkeys_this_block(weight_limit: &Weight) -> Result { let mut weight_used = frame_support::weights::Weight::from_parts(0, 0); - // Get the block number let current_block: u64 = Self::get_current_block_as_u64(); log::debug!("Swapping coldkeys for block: {:?}", current_block); - // Get the coldkeys to swap here and then remove them. let source_coldkeys: Vec = ColdkeysToSwapAtBlock::::get(current_block); ColdkeysToSwapAtBlock::::remove(current_block); weight_used = weight_used.saturating_add(T::DbWeight::get().reads_writes(1, 1)); - // Iterate over all keys in swap and call perform_swap_coldkey for each let mut keys_swapped = 0u64; for coldkey_i in source_coldkeys.iter() { - // Terminate early if we've exhausted the weight limit - // - // We care only about ref_time and not proof_size because we are a solochain. - if weight_used.ref_time() > weight_limit.ref_time() { - log::warn!("Could not finish swapping all coldkeys this block due to weight limit, breaking after swapping {} keys.", keys_swapped); - break; - } + // TODO: need a sane way to terminate early without locking users in. + // we should update the swap time + // if weight_used.ref_time() > weight_limit.ref_time() { + // log::warn!("Could not finish swapping all coldkeys this block due to weight limit, breaking after swapping {} keys.", keys_swapped); + // break; + // } - // Get the wallets to swap to for this coldkey. let destinations_coldkeys: Vec = ColdkeySwapDestinations::::get(coldkey_i); - ColdkeySwapDestinations::::remove(&coldkey_i); - weight_used = weight_used.saturating_add(T::DbWeight::get().reads_writes(1, 1)); + weight_used = weight_used.saturating_add(T::DbWeight::get().reads(1)); - // If the wallets to swap is > 1 we bump the arbitration period. if destinations_coldkeys.len() > 1 { - // Set the arbitration period to u64::MAX until we have a senate vote + // Do not remove ColdkeySwapDestinations if there are multiple destinations ColdkeyArbitrationBlock::::insert(coldkey_i.clone(), u64::MAX); - Self::deposit_event(Event::ArbitrationPeriodExtended { coldkey: coldkey_i.clone(), }); } else if let Some(new_coldkey) = destinations_coldkeys.first() { - // ONLY 1 wallet: Get the wallet to swap to. - // Perform the swap. + // Only remove ColdkeySwapDestinations if there's a single destination + ColdkeySwapDestinations::::remove(&coldkey_i); + weight_used = weight_used.saturating_add(T::DbWeight::get().writes(1)); Self::perform_swap_coldkey(coldkey_i, new_coldkey).map(|weight| { weight_used = weight_used.saturating_add(weight); keys_swapped = keys_swapped.saturating_add(1); From 2891d621a59f0076fef47fc54a832c4a6d45a866 Mon Sep 17 00:00:00 2001 From: Samuel Dare Date: Mon, 8 Jul 2024 23:27:25 +0400 Subject: [PATCH 34/51] chore: fix tests --- pallets/subtensor/src/swap.rs | 4 ++-- pallets/subtensor/tests/staking.rs | 17 +++++++---------- 2 files changed, 9 insertions(+), 12 deletions(-) diff --git a/pallets/subtensor/src/swap.rs b/pallets/subtensor/src/swap.rs index d7a0335e2..34707ebea 100644 --- a/pallets/subtensor/src/swap.rs +++ b/pallets/subtensor/src/swap.rs @@ -305,7 +305,7 @@ impl Pallet { #[allow(clippy::arithmetic_side_effects)] pub fn calculate_pow_difficulty(swap_attempts: u32) -> U256 { let base_difficulty: U256 = U256::from(10_000_000); // Base difficulty - base_difficulty * U256::from(2).pow(U256::from(swap_attempts)) + base_difficulty.saturating_mul(U256::from(2).pow(U256::from(swap_attempts))) } /// Arbitrates coldkeys that are scheduled to be swapped on this block. @@ -317,7 +317,7 @@ impl Pallet { /// # Returns /// /// * `Weight` - The total weight consumed by this operation - pub fn swap_coldkeys_this_block(weight_limit: &Weight) -> Result { + pub fn swap_coldkeys_this_block(_weight_limit: &Weight) -> Result { let mut weight_used = frame_support::weights::Weight::from_parts(0, 0); let current_block: u64 = Self::get_current_block_as_u64(); diff --git a/pallets/subtensor/tests/staking.rs b/pallets/subtensor/tests/staking.rs index c6216b456..caf6c75eb 100644 --- a/pallets/subtensor/tests/staking.rs +++ b/pallets/subtensor/tests/staking.rs @@ -3638,16 +3638,13 @@ fn test_arbitrated_coldkey_swap_existing_destination() { let (work3, nonce3) = generate_valid_pow(¤t_coldkey, current_block, difficulty3); // Attempt to schedule a third swap - assert_noop!( - SubtensorModule::do_schedule_coldkey_swap( - ¤t_coldkey.clone(), - &third_coldkey, - work3.to_fixed_bytes().to_vec(), - current_block, - nonce3 - ), - Error::::ColdkeyIsInArbitration - ); + assert_ok!(SubtensorModule::do_schedule_coldkey_swap( + ¤t_coldkey.clone(), + &third_coldkey, + work3.to_fixed_bytes().to_vec(), + current_block, + nonce3 + )); }); } From d1ee74444b52f24b43ecb4505b933d2f8f5943e7 Mon Sep 17 00:00:00 2001 From: const Date: Mon, 8 Jul 2024 14:44:30 -0500 Subject: [PATCH 35/51] add swap test --- pallets/subtensor/src/swap.rs | 3 +- pallets/subtensor/tests/swap.rs | 129 ++++++++++++++++++++++++++++++++ 2 files changed, 130 insertions(+), 2 deletions(-) diff --git a/pallets/subtensor/src/swap.rs b/pallets/subtensor/src/swap.rs index d7a0335e2..48641b2dc 100644 --- a/pallets/subtensor/src/swap.rs +++ b/pallets/subtensor/src/swap.rs @@ -374,7 +374,6 @@ impl Pallet { // Swap coldkey references in storage maps // NOTE The order of these calls is important - Self::swap_total_coldkey_stake(old_coldkey, new_coldkey, &mut weight); Self::swap_stake_for_coldkey(old_coldkey, new_coldkey, &mut weight); Self::swap_total_hotkey_coldkey_stakes_this_interval_for_coldkey( old_coldkey, @@ -822,7 +821,7 @@ impl Pallet { // Update the total stake for both old and new coldkeys if any stake was transferred if total_transferred_stake > 0 { - let old_coldkey_stake: u64 = TotalColdkeyStake::::get(old_coldkey); + let old_coldkey_stake: u64 = TotalColdkeyStake::::take(old_coldkey); // Remove it here. let new_coldkey_stake: u64 = TotalColdkeyStake::::get(new_coldkey); TotalColdkeyStake::::insert(old_coldkey, 0); diff --git a/pallets/subtensor/tests/swap.rs b/pallets/subtensor/tests/swap.rs index d698e4385..e680c849e 100644 --- a/pallets/subtensor/tests/swap.rs +++ b/pallets/subtensor/tests/swap.rs @@ -1381,6 +1381,135 @@ fn test_coldkey_has_associated_hotkeys() { }); } + + +// SKIP_WASM_BUILD=1 RUST_LOG=info cargo test --test swap -- test_coldkey_swap_total --exact --nocapture +#[test] +fn test_coldkey_swap_total() { + new_test_ext(1).execute_with(|| { + let coldkey = U256::from(1); + let nominator1 = U256::from(2); + let nominator2 = U256::from(3); + let nominator3 = U256::from(4); + let delegate1 = U256::from(5); + let delegate2 = U256::from(6); + let delegate3 = U256::from(7); + let hotkey1 = U256::from(2); + let hotkey2 = U256::from(3); + let hotkey3 = U256::from(4); + let netuid1 = 1u16; + let netuid2 = 2u16; + let netuid3 = 3u16; + SubtensorModule::add_balance_to_coldkey_account(&coldkey, 1000); + SubtensorModule::add_balance_to_coldkey_account(&delegate1, 1000); + SubtensorModule::add_balance_to_coldkey_account(&delegate2, 1000); + SubtensorModule::add_balance_to_coldkey_account(&delegate3, 1000); + SubtensorModule::add_balance_to_coldkey_account(&nominator1, 1000); + SubtensorModule::add_balance_to_coldkey_account(&nominator2, 1000); + SubtensorModule::add_balance_to_coldkey_account(&nominator3, 1000); + + // Setup initial state + add_network(netuid1, 13, 0); + add_network(netuid2, 14, 0); + add_network(netuid3, 15, 0); + register_ok_neuron(netuid1, hotkey1, coldkey, 0); + register_ok_neuron(netuid2, hotkey2, coldkey, 0); + register_ok_neuron(netuid3, hotkey3, coldkey, 0); + register_ok_neuron(netuid1, delegate1, delegate1, 0); + register_ok_neuron(netuid2, delegate2, delegate2, 0); + register_ok_neuron(netuid3, delegate3, delegate3, 0); + assert_ok!(SubtensorModule::do_become_delegate(<::RuntimeOrigin>::signed(coldkey), hotkey1, u16::MAX / 10)); + assert_ok!(SubtensorModule::do_become_delegate(<::RuntimeOrigin>::signed(coldkey), hotkey2, u16::MAX / 10)); + assert_ok!(SubtensorModule::do_become_delegate(<::RuntimeOrigin>::signed(coldkey), hotkey3, u16::MAX / 10)); + assert_ok!(SubtensorModule::do_become_delegate(<::RuntimeOrigin>::signed(delegate1), delegate1, u16::MAX / 10)); + assert_ok!(SubtensorModule::do_become_delegate(<::RuntimeOrigin>::signed(delegate2), delegate2, u16::MAX / 10)); + assert_ok!(SubtensorModule::do_become_delegate(<::RuntimeOrigin>::signed(delegate3), delegate3, u16::MAX / 10)); + + assert_ok!(SubtensorModule::add_stake(<::RuntimeOrigin>::signed(coldkey), hotkey1, 100 )); + assert_ok!(SubtensorModule::add_stake(<::RuntimeOrigin>::signed(coldkey), hotkey2, 100 )); + assert_ok!(SubtensorModule::add_stake(<::RuntimeOrigin>::signed(coldkey), hotkey3, 100 )); + assert_ok!(SubtensorModule::add_stake(<::RuntimeOrigin>::signed(coldkey), delegate1, 100 )); + assert_ok!(SubtensorModule::add_stake(<::RuntimeOrigin>::signed(coldkey), delegate2, 100 )); + assert_ok!(SubtensorModule::add_stake(<::RuntimeOrigin>::signed(coldkey), delegate3, 100 )); + + assert_ok!(SubtensorModule::add_stake(<::RuntimeOrigin>::signed(delegate1), hotkey1, 100 )); + assert_ok!(SubtensorModule::add_stake(<::RuntimeOrigin>::signed(delegate2), hotkey2, 100 )); + assert_ok!(SubtensorModule::add_stake(<::RuntimeOrigin>::signed(delegate3), hotkey3, 100 )); + + assert_ok!(SubtensorModule::add_stake(<::RuntimeOrigin>::signed(delegate1), delegate1, 100 )); + assert_ok!(SubtensorModule::add_stake(<::RuntimeOrigin>::signed(delegate2), delegate2, 100 )); + assert_ok!(SubtensorModule::add_stake(<::RuntimeOrigin>::signed(delegate3), delegate3, 100 )); + + assert_ok!(SubtensorModule::add_stake(<::RuntimeOrigin>::signed(nominator1), hotkey1, 100 )); + assert_ok!(SubtensorModule::add_stake(<::RuntimeOrigin>::signed(nominator2), hotkey2, 100 )); + assert_ok!(SubtensorModule::add_stake(<::RuntimeOrigin>::signed(nominator3), hotkey3, 100 )); + + assert_ok!(SubtensorModule::add_stake(<::RuntimeOrigin>::signed(nominator1), delegate1, 100 )); + assert_ok!(SubtensorModule::add_stake(<::RuntimeOrigin>::signed(nominator2), delegate2, 100 )); + assert_ok!(SubtensorModule::add_stake(<::RuntimeOrigin>::signed(nominator3), delegate3, 100 )); + + assert_eq!( SubtensorModule::get_owned_hotkeys(&coldkey), vec![hotkey1, hotkey2, hotkey3] ); + assert_eq!( SubtensorModule::get_all_staked_hotkeys(&coldkey), vec![hotkey1, hotkey2, hotkey3, delegate1, delegate2, delegate3] ); + assert_eq!( SubtensorModule::get_total_stake_for_coldkey(&coldkey), 600 ); + assert_eq!( SubtensorModule::get_total_stake_for_hotkey(&hotkey1), 300 ); + assert_eq!( SubtensorModule::get_total_stake_for_hotkey(&hotkey2), 300 ); + assert_eq!( SubtensorModule::get_total_stake_for_hotkey(&hotkey3), 300 ); + assert_eq!( SubtensorModule::get_total_stake_for_hotkey(&delegate1), 300 ); + assert_eq!( SubtensorModule::get_total_stake_for_hotkey(&delegate2), 300 ); + assert_eq!( SubtensorModule::get_total_stake_for_hotkey(&delegate3), 300 ); + + + assert_eq!( SubtensorModule::get_owned_hotkeys(&delegate1), vec![delegate1] ); + assert_eq!( SubtensorModule::get_owned_hotkeys(&delegate2), vec![delegate2] ); + assert_eq!( SubtensorModule::get_owned_hotkeys(&delegate3), vec![delegate3] ); + assert_eq!( SubtensorModule::get_all_staked_hotkeys(&delegate1), vec![delegate1, hotkey1] ); + assert_eq!( SubtensorModule::get_all_staked_hotkeys(&delegate2), vec![delegate2, hotkey2] ); + assert_eq!( SubtensorModule::get_all_staked_hotkeys(&delegate3), vec![delegate3, hotkey3] ); + + assert_eq!( SubtensorModule::get_owned_hotkeys(&nominator1), vec![] ); + assert_eq!( SubtensorModule::get_owned_hotkeys(&nominator2), vec![] ); + assert_eq!( SubtensorModule::get_owned_hotkeys(&nominator3), vec![] ); + + assert_eq!( SubtensorModule::get_all_staked_hotkeys(&nominator1), vec![hotkey1, delegate1] ); + assert_eq!( SubtensorModule::get_all_staked_hotkeys(&nominator2), vec![hotkey2, delegate2] ); + assert_eq!( SubtensorModule::get_all_staked_hotkeys(&nominator3), vec![hotkey3, delegate3] ); + + // Perform the swap + let new_coldkey = U256::from(1100); + assert_eq!( SubtensorModule::get_total_stake_for_coldkey(&coldkey), 600 ); + assert_ok!(SubtensorModule::perform_swap_coldkey( &coldkey, &new_coldkey )); + assert_eq!( SubtensorModule::get_total_stake_for_coldkey(&new_coldkey), 600 ); + + // Check everything is swapped. + assert_eq!( SubtensorModule::get_owned_hotkeys(&new_coldkey), vec![hotkey1, hotkey2, hotkey3] ); + assert_eq!( SubtensorModule::get_all_staked_hotkeys(&new_coldkey), vec![hotkey1, hotkey2, hotkey3, delegate1, delegate2, delegate3] ); + assert_eq!( SubtensorModule::get_total_stake_for_coldkey(&new_coldkey), 600 ); + assert_eq!( SubtensorModule::get_total_stake_for_hotkey(&hotkey1), 300 ); + assert_eq!( SubtensorModule::get_total_stake_for_hotkey(&hotkey2), 300 ); + assert_eq!( SubtensorModule::get_total_stake_for_hotkey(&hotkey3), 300 ); + assert_eq!( SubtensorModule::get_total_stake_for_hotkey(&delegate1), 300 ); + assert_eq!( SubtensorModule::get_total_stake_for_hotkey(&delegate2), 300 ); + assert_eq!( SubtensorModule::get_total_stake_for_hotkey(&delegate3), 300 ); + + assert_eq!( SubtensorModule::get_owned_hotkeys(&delegate1), vec![delegate1] ); + assert_eq!( SubtensorModule::get_owned_hotkeys(&delegate2), vec![delegate2] ); + assert_eq!( SubtensorModule::get_owned_hotkeys(&delegate3), vec![delegate3] ); + assert_eq!( SubtensorModule::get_all_staked_hotkeys(&delegate1), vec![delegate1, hotkey1] ); + assert_eq!( SubtensorModule::get_all_staked_hotkeys(&delegate2), vec![delegate2, hotkey2] ); + assert_eq!( SubtensorModule::get_all_staked_hotkeys(&delegate3), vec![delegate3, hotkey3] ); + + assert_eq!( SubtensorModule::get_owned_hotkeys(&nominator1), vec![] ); + assert_eq!( SubtensorModule::get_owned_hotkeys(&nominator2), vec![] ); + assert_eq!( SubtensorModule::get_owned_hotkeys(&nominator3), vec![] ); + + assert_eq!( SubtensorModule::get_all_staked_hotkeys(&nominator1), vec![hotkey1, delegate1] ); + assert_eq!( SubtensorModule::get_all_staked_hotkeys(&nominator2), vec![hotkey2, delegate2] ); + assert_eq!( SubtensorModule::get_all_staked_hotkeys(&nominator3), vec![hotkey3, delegate3] ); + + + }); +} + // #[test] // fn test_coldkey_arbitrated_sw() { // new_test_ext(1).execute_with(|| { From 4a97e7953047b0cb319a5244fa5408162e850c93 Mon Sep 17 00:00:00 2001 From: const Date: Mon, 8 Jul 2024 14:56:34 -0500 Subject: [PATCH 36/51] working --- pallets/subtensor/src/lib.rs | 3 +-- pallets/subtensor/src/swap.rs | 7 +++---- pallets/subtensor/tests/swap.rs | 2 -- 3 files changed, 4 insertions(+), 8 deletions(-) diff --git a/pallets/subtensor/src/lib.rs b/pallets/subtensor/src/lib.rs index 4c0efd78f..d57445824 100644 --- a/pallets/subtensor/src/lib.rs +++ b/pallets/subtensor/src/lib.rs @@ -2059,10 +2059,9 @@ pub mod pallet { .saturating_add(T::DbWeight::get().writes(527)), DispatchClass::Operational, Pays::No))] pub fn swap_coldkey( origin: OriginFor, - old_coldkey: T::AccountId, new_coldkey: T::AccountId, ) -> DispatchResultWithPostInfo { - Self::do_swap_coldkey(origin, &old_coldkey, &new_coldkey) + Self::do_swap_coldkey(origin, &new_coldkey) } /// Unstakes all tokens associated with a hotkey and transfers them to a new coldkey. diff --git a/pallets/subtensor/src/swap.rs b/pallets/subtensor/src/swap.rs index b56e6f4ea..07cde1d69 100644 --- a/pallets/subtensor/src/swap.rs +++ b/pallets/subtensor/src/swap.rs @@ -119,12 +119,11 @@ impl Pallet { /// Weight is tracked and updated throughout the function execution. pub fn do_swap_coldkey( origin: T::RuntimeOrigin, - old_coldkey: &T::AccountId, new_coldkey: &T::AccountId, ) -> DispatchResultWithPostInfo { - let coldkey_performing_swap = ensure_signed(origin)?; + let old_coldkey = ensure_signed(origin)?; ensure!( - !Self::coldkey_in_arbitration(&coldkey_performing_swap), + !Self::coldkey_in_arbitration(&old_coldkey), Error::::ColdkeyIsInArbitration ); @@ -143,7 +142,7 @@ impl Pallet { // Actually do the swap. weight = weight.saturating_add( - Self::perform_swap_coldkey(old_coldkey, new_coldkey) + Self::perform_swap_coldkey(&old_coldkey, new_coldkey) .map_err(|_| Error::::ColdkeySwapError)?, ); diff --git a/pallets/subtensor/tests/swap.rs b/pallets/subtensor/tests/swap.rs index e680c849e..a389b5bd9 100644 --- a/pallets/subtensor/tests/swap.rs +++ b/pallets/subtensor/tests/swap.rs @@ -1118,7 +1118,6 @@ fn test_do_swap_coldkey_success() { // Perform the swap assert_ok!(SubtensorModule::do_swap_coldkey( <::RuntimeOrigin>::signed(old_coldkey), - &old_coldkey, &new_coldkey )); @@ -1350,7 +1349,6 @@ fn test_do_swap_coldkey_with_subnet_ownership() { // Perform the swap assert_ok!(SubtensorModule::do_swap_coldkey( <::RuntimeOrigin>::signed(old_coldkey), - &old_coldkey, &new_coldkey )); From ba00c231f6ef4dc82407e951cf9a3f1c6d9eac83 Mon Sep 17 00:00:00 2001 From: const Date: Mon, 8 Jul 2024 15:23:45 -0500 Subject: [PATCH 37/51] add stake balance as equal --- pallets/subtensor/src/swap.rs | 12 +++++++++- pallets/subtensor/tests/staking.rs | 38 ++++++++++++++++++++++++++++++ pallets/subtensor/tests/swap.rs | 10 +------- 3 files changed, 50 insertions(+), 10 deletions(-) diff --git a/pallets/subtensor/src/swap.rs b/pallets/subtensor/src/swap.rs index 07cde1d69..b65e5cc43 100644 --- a/pallets/subtensor/src/swap.rs +++ b/pallets/subtensor/src/swap.rs @@ -199,6 +199,16 @@ impl Pallet { } } + pub fn meets_min_allowed_coldkey_balance( coldkey: &T::AccountId ) -> bool { + let all_staked_keys: Vec = StakingHotkeys::::get(coldkey); + let mut total_staking_balance: u64 = 0; + for hotkey in all_staked_keys { + total_staking_balance += Self::get_stake_for_coldkey_and_hotkey(&coldkey, &hotkey); + } + total_staking_balance += Self::get_coldkey_balance(&coldkey); + total_staking_balance >= MIN_BALANCE_TO_PERFORM_COLDKEY_SWAP + } + /// Schedules a coldkey swap to a new coldkey with arbitration. /// /// # Arguments @@ -238,7 +248,7 @@ impl Pallet { // Check minimum amount of TAO (1 TAO) ensure!( - Self::get_coldkey_balance(old_coldkey) >= MIN_BALANCE_TO_PERFORM_COLDKEY_SWAP, + Self::meets_min_allowed_coldkey_balance(&old_coldkey), Error::::InsufficientBalanceToPerformColdkeySwap ); diff --git a/pallets/subtensor/tests/staking.rs b/pallets/subtensor/tests/staking.rs index caf6c75eb..3ead79d88 100644 --- a/pallets/subtensor/tests/staking.rs +++ b/pallets/subtensor/tests/staking.rs @@ -3980,3 +3980,41 @@ fn next_block() { System::on_initialize(System::block_number()); SubtensorModule::on_initialize(System::block_number()); } + +// SKIP_WASM_BUILD=1 RUST_LOG=info cargo test --test staking -- test_coldkey_meets_enough --exact --nocapture +#[test] +fn test_coldkey_meets_enough() { + new_test_ext(1).execute_with(|| { + let coldkey = U256::from(1); + let new_coldkey = U256::from(2); + let hotkey = U256::from(2); + let netuid = 1u16; + add_network(netuid, 13, 0); + register_ok_neuron(netuid, hotkey, coldkey, 0); + let current_block = SubtensorModule::get_current_block_as_u64(); + let (work1, nonce1) = generate_valid_pow(&coldkey, current_block, U256::from(10_000_000u64)); + assert_err!( + SubtensorModule::do_schedule_coldkey_swap( + &coldkey.clone(), + &new_coldkey, + work1.to_fixed_bytes().to_vec(), + current_block, + nonce1 + ), + Error::::InsufficientBalanceToPerformColdkeySwap + ); + SubtensorModule::add_balance_to_coldkey_account( + &coldkey, + MIN_BALANCE_TO_PERFORM_COLDKEY_SWAP, + ); + assert_ok!(SubtensorModule::do_schedule_coldkey_swap( + &coldkey.clone(), + &new_coldkey, + work1.to_fixed_bytes().to_vec(), + current_block, + nonce1 + )); + + + }); +} diff --git a/pallets/subtensor/tests/swap.rs b/pallets/subtensor/tests/swap.rs index a389b5bd9..c306089bf 100644 --- a/pallets/subtensor/tests/swap.rs +++ b/pallets/subtensor/tests/swap.rs @@ -1367,20 +1367,12 @@ fn test_coldkey_has_associated_hotkeys() { // Setup initial state add_network(netuid, 13, 0); register_ok_neuron(netuid, hotkey, coldkey, 0); + SubtensorModule::add_balance_to_coldkey_account(&coldkey, 1000); - // Check if coldkey has associated hotkeys - assert!(SubtensorModule::coldkey_has_associated_hotkeys(&coldkey)); - - // Check for a coldkey without associated hotkeys - let unassociated_coldkey = U256::from(3); - assert!(!SubtensorModule::coldkey_has_associated_hotkeys( - &unassociated_coldkey - )); }); } - // SKIP_WASM_BUILD=1 RUST_LOG=info cargo test --test swap -- test_coldkey_swap_total --exact --nocapture #[test] fn test_coldkey_swap_total() { From 41feafc908a2133dd54d599d1d8a4b4f9771bb83 Mon Sep 17 00:00:00 2001 From: Samuel Dare Date: Tue, 9 Jul 2024 00:24:27 +0400 Subject: [PATCH 38/51] chore: bump 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 0afaec385..cdd8f7df6 100644 --- a/runtime/src/lib.rs +++ b/runtime/src/lib.rs @@ -139,7 +139,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: 161, + spec_version: 188, impl_version: 1, apis: RUNTIME_API_VERSIONS, transaction_version: 1, From b1feb3cbc9d5f276bc8e2d8bec9fd92756735f74 Mon Sep 17 00:00:00 2001 From: Samuel Dare Date: Tue, 9 Jul 2024 01:22:49 +0400 Subject: [PATCH 39/51] feat: reduce arb period to 18 hours for testnet --- pallets/subtensor/src/lib.rs | 6 ++++-- pallets/subtensor/tests/staking.rs | 18 +++++++++--------- runtime/src/lib.rs | 2 +- 3 files changed, 14 insertions(+), 12 deletions(-) diff --git a/pallets/subtensor/src/lib.rs b/pallets/subtensor/src/lib.rs index d57445824..ccce8cb5a 100644 --- a/pallets/subtensor/src/lib.rs +++ b/pallets/subtensor/src/lib.rs @@ -394,9 +394,11 @@ pub mod pallet { vec![] } #[pallet::type_value] - /// Default stake interval. + /// Default arbitration period. + /// This value represents the default arbitration period in blocks. + /// The period is set to 18 hours, assuming a block time of 12 seconds. pub fn DefaultArbitrationPeriod() -> u64 { - 7200 * 3 + 5400 // 18 hours * 60 minutes/hour * 5 blocks/minute } #[pallet::storage] // ---- StorageItem Global Used Work. pub type ArbitrationPeriod = diff --git a/pallets/subtensor/tests/staking.rs b/pallets/subtensor/tests/staking.rs index 3ead79d88..829762417 100644 --- a/pallets/subtensor/tests/staking.rs +++ b/pallets/subtensor/tests/staking.rs @@ -3188,7 +3188,7 @@ fn test_arbitrated_coldkey_swap_success() { ); // Check that drain block is set correctly - let drain_block: u64 = 7200 * 3 + 1; + let drain_block: u64 = 5400 + 1; log::info!( "ColdkeysToSwapAtBlock before scheduling: {:?}", @@ -3205,7 +3205,7 @@ fn test_arbitrated_coldkey_swap_success() { pallet_subtensor::ColdkeysToSwapAtBlock::::get(drain_block) ); - // Make 7200 * 4 blocks pass + // Make 5400 blocks pass run_to_block(drain_block); // Run unstaking @@ -3385,8 +3385,8 @@ fn test_arbitrated_coldkey_swap_with_no_stake() { nonce )); - // Make 7200 * 4 blocks pass, simulating on_idle for each block - let drain_block: u64 = 7200 * 3 + 1; + // Make 5400 blocks pass, simulating on_idle for each block + let drain_block: u64 = 5400 + 1; for _ in 0..drain_block { next_block(); SubtensorModule::on_idle(System::block_number(), Weight::MAX); @@ -3446,8 +3446,8 @@ fn test_arbitrated_coldkey_swap_with_multiple_stakes() { nonce )); - // Make 7200 * 4 blocks pass, simulating on_idle for each block - let drain_block: u64 = 7200 * 3 + 1; + // Make 5400 blocks pass, simulating on_idle for each block + let drain_block: u64 = 5400 + 1; for _ in 0..drain_block { next_block(); SubtensorModule::on_idle(System::block_number(), Weight::MAX); @@ -3530,7 +3530,7 @@ fn test_arbitrated_coldkey_swap_multiple_arbitrations() { ); // Simulate the passage of blocks and on_idle calls - for i in 0..(7200 * 3 + 1) { + for i in 0..(5400 + 1) { // Simulate 10 blocks next_block(); SubtensorModule::on_idle(System::block_number(), Weight::MAX); @@ -3744,8 +3744,8 @@ fn test_concurrent_arbitrated_coldkey_swaps() { current_block, nonce2 )); - // Make 7200 * 3 blocks pass - let drain_block: u64 = 7200 * 3 + 1; + // Make 5400 blocks pass + let drain_block: u64 = 5400 + 1; run_to_block(drain_block); // Run arbitration diff --git a/runtime/src/lib.rs b/runtime/src/lib.rs index cdd8f7df6..ea4dded94 100644 --- a/runtime/src/lib.rs +++ b/runtime/src/lib.rs @@ -139,7 +139,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: 188, + spec_version: 189, impl_version: 1, apis: RUNTIME_API_VERSIONS, transaction_version: 1, From e4c9ef2e9518feb0bc03dca7ae2a74489fd9164d Mon Sep 17 00:00:00 2001 From: Samuel Dare Date: Tue, 9 Jul 2024 03:01:19 +0400 Subject: [PATCH 40/51] feat: allow subnet owners to swap without funds , todo: fix test_comprehensive_coldkey_swap_scenarios --- pallets/subtensor/src/swap.rs | 16 ++- pallets/subtensor/tests/staking.rs | 188 ++++++++++++++++++++++++++++- 2 files changed, 197 insertions(+), 7 deletions(-) diff --git a/pallets/subtensor/src/swap.rs b/pallets/subtensor/src/swap.rs index b65e5cc43..4dd78d2e0 100644 --- a/pallets/subtensor/src/swap.rs +++ b/pallets/subtensor/src/swap.rs @@ -246,11 +246,17 @@ impl Pallet { ) -> DispatchResult { ensure!(old_coldkey != new_coldkey, Error::::SameColdkey); - // Check minimum amount of TAO (1 TAO) - ensure!( - Self::meets_min_allowed_coldkey_balance(&old_coldkey), - Error::::InsufficientBalanceToPerformColdkeySwap - ); + // Check if the old_coldkey is a subnet owner for any network + let is_subnet_owner = (0..=TotalNetworks::::get()) + .any(|netuid| SubnetOwner::::get(netuid) == *old_coldkey); + + // Only check the minimum balance if the old_coldkey is not a subnet owner + if !is_subnet_owner { + ensure!( + Self::meets_min_allowed_coldkey_balance(&old_coldkey), + Error::::InsufficientBalanceToPerformColdkeySwap + ); + } // Get current destination coldkeys let mut destination_coldkeys: Vec = diff --git a/pallets/subtensor/tests/staking.rs b/pallets/subtensor/tests/staking.rs index 829762417..7d52011c7 100644 --- a/pallets/subtensor/tests/staking.rs +++ b/pallets/subtensor/tests/staking.rs @@ -3992,7 +3992,8 @@ fn test_coldkey_meets_enough() { add_network(netuid, 13, 0); register_ok_neuron(netuid, hotkey, coldkey, 0); let current_block = SubtensorModule::get_current_block_as_u64(); - let (work1, nonce1) = generate_valid_pow(&coldkey, current_block, U256::from(10_000_000u64)); + let (work1, nonce1) = + generate_valid_pow(&coldkey, current_block, U256::from(10_000_000u64)); assert_err!( SubtensorModule::do_schedule_coldkey_swap( &coldkey.clone(), @@ -4014,7 +4015,190 @@ fn test_coldkey_meets_enough() { current_block, nonce1 )); - + }); +} + +#[test] +fn test_comprehensive_coldkey_swap_scenarios() { + new_test_ext(1).execute_with(|| { + // Set arbitration period to 5 blocks + ArbitrationPeriod::::put(5); + + let subnet_owner1 = U256::from(1); + let subnet_owner2 = U256::from(2); + let regular_user = U256::from(3); + let new_coldkey1 = U256::from(4); + let new_coldkey2 = U256::from(5); + let new_coldkey3 = U256::from(6); + let netuid1 = 1; + let netuid2 = 2; + + // Add networks and register subnet owners + add_network(netuid1, 13, 0); + add_network(netuid2, 13, 0); + SubnetOwner::::insert(netuid1, subnet_owner1); + SubnetOwner::::insert(netuid2, subnet_owner2); + + // Add balance to regular user + SubtensorModule::add_balance_to_coldkey_account( + ®ular_user, + MIN_BALANCE_TO_PERFORM_COLDKEY_SWAP * 2, + ); + let current_block = SubtensorModule::get_current_block_as_u64(); + + // Schedule swaps for subnet owners and regular user + let (work1, nonce1) = + generate_valid_pow(&subnet_owner1, current_block, U256::from(10_000_000u64)); + let (work2, nonce2) = + generate_valid_pow(&subnet_owner2, current_block, U256::from(20_000_000u64)); + let (work3, nonce3) = + generate_valid_pow(®ular_user, current_block, U256::from(30_000_000u64)); + + assert_ok!(SubtensorModule::do_schedule_coldkey_swap( + &subnet_owner1, + &new_coldkey1, + work1.to_fixed_bytes().to_vec(), + current_block, + nonce1 + )); + + assert_ok!(SubtensorModule::do_schedule_coldkey_swap( + &subnet_owner2, + &new_coldkey2, + work2.to_fixed_bytes().to_vec(), + current_block, + nonce2 + )); + + assert_ok!(SubtensorModule::do_schedule_coldkey_swap( + ®ular_user, + &new_coldkey3, + work3.to_fixed_bytes().to_vec(), + current_block, + nonce3 + )); + + // Check if swaps were scheduled correctly + assert_eq!( + ColdkeySwapDestinations::::get(subnet_owner1), + vec![new_coldkey1] + ); + assert_eq!( + ColdkeySwapDestinations::::get(subnet_owner2), + vec![new_coldkey2] + ); + assert_eq!( + ColdkeySwapDestinations::::get(regular_user), + vec![new_coldkey3] + ); + + // Run through the arbitration period plus one block + for i in 0..6 { + next_block(); + SubtensorModule::on_idle(System::block_number(), Weight::MAX); + + log::info!( + "Block {}: Coldkey in arbitration: {}, Swap destinations: {:?}", + i + 1, + SubtensorModule::coldkey_in_arbitration(&subnet_owner1), + ColdkeySwapDestinations::::get(subnet_owner1) + ); + + // Test edge case: try to schedule another swap during arbitration + if i == 2 { + let (work4, nonce4) = generate_valid_pow( + &subnet_owner1, + current_block + i as u64, + U256::from(40_000_000u64), + ); + assert_ok!(SubtensorModule::do_schedule_coldkey_swap( + &subnet_owner1, + &new_coldkey2, + work4.to_fixed_bytes().to_vec(), + current_block + i as u64, + nonce4 + )); + // This should add new_coldkey2 to subnet_owner1's destinations + assert_eq!( + ColdkeySwapDestinations::::get(subnet_owner1), + vec![new_coldkey1, new_coldkey2] + ); + } + } + + // Check if swaps have been executed + log::info!( + "After arbitration period - Swap destinations for subnet_owner1: {:?}", + ColdkeySwapDestinations::::get(subnet_owner1) + ); + assert_eq!( + ColdkeySwapDestinations::::get(subnet_owner1), + vec![new_coldkey1, new_coldkey2], + "ColdkeySwapDestinations for subnet_owner1 should still contain two destinations after arbitration period" + ); + assert!(ColdkeySwapDestinations::::get(subnet_owner2).is_empty()); + assert!(ColdkeySwapDestinations::::get(regular_user).is_empty()); + + // Verify that subnet ownerships have NOT been transferred for subnet_owner1 + assert_eq!(SubnetOwner::::get(netuid1), subnet_owner1); + // But subnet_owner2's ownership should have been transferred + assert_eq!(SubnetOwner::::get(netuid2), new_coldkey2); + + // Verify regular user's balance has been transferred + assert_eq!( + SubtensorModule::get_coldkey_balance(&new_coldkey3), + MIN_BALANCE_TO_PERFORM_COLDKEY_SWAP * 2 + ); + assert_eq!(SubtensorModule::get_coldkey_balance(®ular_user), 0); + + // Test multiple calls for the same subnet owner + let (work5, nonce5) = + generate_valid_pow(&new_coldkey1, current_block + 7, U256::from(50_000_000u64)); + let (work6, nonce6) = + generate_valid_pow(&new_coldkey1, current_block + 7, U256::from(60_000_000u64)); + + assert_ok!(SubtensorModule::do_schedule_coldkey_swap( + &new_coldkey1, + &subnet_owner1, + work5.to_fixed_bytes().to_vec(), + current_block + 7, + nonce5 + )); + + assert_ok!(SubtensorModule::do_schedule_coldkey_swap( + &new_coldkey1, + &subnet_owner2, + work6.to_fixed_bytes().to_vec(), + current_block + 7, + nonce6 + )); + + assert_eq!( + ColdkeySwapDestinations::::get(new_coldkey1), + vec![subnet_owner1, subnet_owner2] + ); + + // Run through another arbitration period plus one block + for i in 0..6 { + next_block(); + SubtensorModule::on_idle(System::block_number(), Weight::MAX); + + log::info!( + "Block {}: Coldkey in arbitration: {}, Swap destinations: {:?}", + i + 7, + SubtensorModule::coldkey_in_arbitration(&new_coldkey1), + ColdkeySwapDestinations::::get(new_coldkey1) + ); + } + + // Check final state + log::info!( + "Final state - Swap destinations for new_coldkey1: {:?}", + ColdkeySwapDestinations::::get(new_coldkey1) + ); + assert!(ColdkeySwapDestinations::::get(new_coldkey1).is_empty()); + assert_eq!(SubnetOwner::::get(netuid1), subnet_owner1); + assert_eq!(SubnetOwner::::get(netuid2), subnet_owner2); }); } From af555d727bf205e792e920591be27927eb002206 Mon Sep 17 00:00:00 2001 From: Samuel Dare Date: Tue, 9 Jul 2024 14:35:13 +0400 Subject: [PATCH 41/51] chore: introduce difficulty var to make tests shorter , fix test_comprehensive_coldkey_swap_scenarios --- pallets/subtensor/src/lib.rs | 13 ++ pallets/subtensor/src/swap.rs | 16 +- pallets/subtensor/tests/mock.rs | 2 + pallets/subtensor/tests/staking.rs | 169 +++++++-------- pallets/subtensor/tests/swap.rs | 334 +++++++++++++++++++++-------- runtime/src/lib.rs | 2 + 6 files changed, 356 insertions(+), 180 deletions(-) diff --git a/pallets/subtensor/src/lib.rs b/pallets/subtensor/src/lib.rs index ccce8cb5a..8eddc4937 100644 --- a/pallets/subtensor/src/lib.rs +++ b/pallets/subtensor/src/lib.rs @@ -87,6 +87,7 @@ pub mod pallet { /// Minimum balance required to perform a coldkey swap pub const MIN_BALANCE_TO_PERFORM_COLDKEY_SWAP: u64 = 1_000_000_000; // 1 TAO in RAO + #[pallet::pallet] #[pallet::without_storage_info] #[pallet::storage_version(STORAGE_VERSION)] @@ -255,6 +256,9 @@ pub mod pallet { /// A flag to indicate if Liquid Alpha is enabled. #[pallet::constant] type LiquidAlphaOn: Get; + /// The base difficulty for proof of work for coldkey swaps + #[pallet::constant] + type InitialBaseDifficulty: Get; } /// Alias for the account ID. @@ -331,6 +335,12 @@ pub mod pallet { 360 } + /// Default base difficulty for proof of work for coldkey swaps + #[pallet::type_value] + pub fn DefaultBaseDifficulty() -> u64 { + T::InitialBaseDifficulty::get() + } + #[pallet::storage] // --- ITEM ( total_stake ) pub type TotalStake = StorageValue<_, u64, ValueQuery>; #[pallet::storage] // --- ITEM ( default_take ) @@ -344,6 +354,9 @@ pub mod pallet { #[pallet::storage] // --- ITEM (target_stakes_per_interval) pub type TargetStakesPerInterval = StorageValue<_, u64, ValueQuery, DefaultTargetStakesPerInterval>; + + #[pallet::storage] // --- ITEM ( base_difficulty ) + pub type BaseDifficulty = StorageValue<_, u64, ValueQuery, DefaultBaseDifficulty>; #[pallet::storage] // --- ITEM (default_stake_interval) pub type StakeInterval = StorageValue<_, u64, ValueQuery, DefaultStakeInterval>; #[pallet::storage] // --- MAP ( hot ) --> stake | Returns the total amount of stake under a hotkey. diff --git a/pallets/subtensor/src/swap.rs b/pallets/subtensor/src/swap.rs index 4dd78d2e0..1a272f216 100644 --- a/pallets/subtensor/src/swap.rs +++ b/pallets/subtensor/src/swap.rs @@ -199,7 +199,7 @@ impl Pallet { } } - pub fn meets_min_allowed_coldkey_balance( coldkey: &T::AccountId ) -> bool { + pub fn meets_min_allowed_coldkey_balance(coldkey: &T::AccountId) -> bool { let all_staked_keys: Vec = StakingHotkeys::::get(coldkey); let mut total_staking_balance: u64 = 0; for hotkey in all_staked_keys { @@ -225,12 +225,12 @@ impl Pallet { /// /// # Errors /// - /// This function will return an error if: - /// - The old coldkey is the same as the new coldkey. - /// - The new coldkey is already in the list of destination coldkeys. - /// - There are already 2 destination coldkeys for the old coldkey. - /// - The old coldkey doesn't have the minimum required TAO balance. - /// - The proof of work is invalid or doesn't meet the required difficulty. + + /// - `SameColdkey`: The old coldkey is the same as the new coldkey. + /// - `DuplicateColdkey`: The new coldkey is already in the list of destination coldkeys. + /// - `MaxColdkeyDestinationsReached`: There are already the maximum allowed destination coldkeys for the old coldkey. + /// - `InsufficientBalanceToPerformColdkeySwap`: The old coldkey doesn't have the minimum required TAO balance. + /// - `InvalidDifficulty`: The proof of work is invalid or doesn't meet the required difficulty. /// /// # Notes /// @@ -319,7 +319,7 @@ impl Pallet { /// Calculate the proof of work difficulty based on the number of swap attempts #[allow(clippy::arithmetic_side_effects)] pub fn calculate_pow_difficulty(swap_attempts: u32) -> U256 { - let base_difficulty: U256 = U256::from(10_000_000); // Base difficulty + let base_difficulty: U256 = U256::from(BaseDifficulty::::get()); // Base difficulty base_difficulty.saturating_mul(U256::from(2).pow(U256::from(swap_attempts))) } diff --git a/pallets/subtensor/tests/mock.rs b/pallets/subtensor/tests/mock.rs index 9e21f2a63..c3a864075 100644 --- a/pallets/subtensor/tests/mock.rs +++ b/pallets/subtensor/tests/mock.rs @@ -168,6 +168,7 @@ parameter_types! { pub const InitialAlphaHigh: u16 = 58982; // Represents 0.9 as per the production default pub const InitialAlphaLow: u16 = 45875; // Represents 0.7 as per the production default pub const InitialLiquidAlphaOn: bool = false; // Default value for LiquidAlphaOn + pub const SubtensorInitialBaseDifficulty: u64 = 10_000; // Base difficulty } // Configure collective pallet for council @@ -372,6 +373,7 @@ impl pallet_subtensor::Config for Test { type AlphaHigh = InitialAlphaHigh; type AlphaLow = InitialAlphaLow; type LiquidAlphaOn = InitialLiquidAlphaOn; + type InitialBaseDifficulty = SubtensorInitialBaseDifficulty; } impl pallet_utility::Config for Test { diff --git a/pallets/subtensor/tests/staking.rs b/pallets/subtensor/tests/staking.rs index 7d52011c7..9d3a4698b 100644 --- a/pallets/subtensor/tests/staking.rs +++ b/pallets/subtensor/tests/staking.rs @@ -3167,8 +3167,11 @@ fn test_arbitrated_coldkey_swap_success() { let (current_coldkey, hotkey, new_coldkey) = setup_test_environment(); let current_block = SubtensorModule::get_current_block_as_u64(); - let (work, nonce) = - generate_valid_pow(¤t_coldkey, current_block, U256::from(10_000_000u64)); + let (work, nonce) = generate_valid_pow( + ¤t_coldkey, + current_block, + U256::from(BaseDifficulty::::get()), + ); SubtensorModule::add_balance_to_coldkey_account( ¤t_coldkey, MIN_BALANCE_TO_PERFORM_COLDKEY_SWAP, @@ -3239,8 +3242,11 @@ fn test_arbitrated_coldkey_swap_same_coldkey() { let (current_coldkey, _hotkey, _) = setup_test_environment(); let current_block = SubtensorModule::get_current_block_as_u64(); - let (work, nonce) = - generate_valid_pow(¤t_coldkey, current_block, U256::from(10_000_000u64)); + let (work, nonce) = generate_valid_pow( + ¤t_coldkey, + current_block, + U256::from(BaseDifficulty::::get()), + ); assert_noop!( SubtensorModule::do_schedule_coldkey_swap( @@ -3290,8 +3296,11 @@ fn test_arbitrated_coldkey_swap_no_balance() { // Generate valid PoW let current_block = SubtensorModule::get_current_block_as_u64(); - let (work, nonce) = - generate_valid_pow(¤t_coldkey, current_block, U256::from(10_000_000u64)); + let (work, nonce) = generate_valid_pow( + ¤t_coldkey, + current_block, + U256::from(BaseDifficulty::::get()), + ); // Try to schedule coldkey swap let result = SubtensorModule::do_schedule_coldkey_swap( @@ -3373,8 +3382,11 @@ fn test_arbitrated_coldkey_swap_with_no_stake() { assert_eq!(Balances::total_balance(&new_coldkey), 0); let current_block = SubtensorModule::get_current_block_as_u64(); - let (work, nonce) = - generate_valid_pow(¤t_coldkey, current_block, U256::from(10_000_000u64)); + let (work, nonce) = generate_valid_pow( + ¤t_coldkey, + current_block, + U256::from(BaseDifficulty::::get()), + ); // Schedule coldkey swap assert_ok!(SubtensorModule::do_schedule_coldkey_swap( @@ -3435,8 +3447,11 @@ fn test_arbitrated_coldkey_swap_with_multiple_stakes() { )); let current_block = SubtensorModule::get_current_block_as_u64(); - let (work, nonce) = - generate_valid_pow(¤t_coldkey, current_block, U256::from(10_000_000u64)); + let (work, nonce) = generate_valid_pow( + ¤t_coldkey, + current_block, + U256::from(BaseDifficulty::::get()), + ); assert_ok!(SubtensorModule::do_schedule_coldkey_swap( ¤t_coldkey.clone(), @@ -3476,6 +3491,9 @@ fn test_arbitrated_coldkey_swap_with_multiple_stakes() { #[test] fn test_arbitrated_coldkey_swap_multiple_arbitrations() { new_test_ext(1).execute_with(|| { + // Set a very low base difficulty for testing + BaseDifficulty::::put(1); + // Create coldkey with three choices. let coldkey: AccountId = U256::from(1); let new_coldkey1: AccountId = U256::from(2); @@ -3493,12 +3511,11 @@ fn test_arbitrated_coldkey_swap_multiple_arbitrations() { register_ok_neuron(1, hotkey, coldkey, 0); let current_block = SubtensorModule::get_current_block_as_u64(); - let (work1, nonce1) = - generate_valid_pow(&coldkey, current_block, U256::from(10_000_000u64)); - let (work2, nonce2) = - generate_valid_pow(&coldkey, current_block, U256::from(20_000_000u64)); - let (work3, nonce3) = - generate_valid_pow(&coldkey, current_block, U256::from(30_000_000u64)); + + // Generate valid PoW for each swap attempt + let (work1, nonce1) = generate_valid_pow(&coldkey, current_block, U256::from(1)); + let (work2, nonce2) = generate_valid_pow(&coldkey, current_block, U256::from(2)); + let (work3, nonce3) = generate_valid_pow(&coldkey, current_block, U256::from(4)); // Schedule three swaps assert_ok!(SubtensorModule::do_schedule_coldkey_swap( @@ -3531,7 +3548,6 @@ fn test_arbitrated_coldkey_swap_multiple_arbitrations() { // Simulate the passage of blocks and on_idle calls for i in 0..(5400 + 1) { - // Simulate 10 blocks next_block(); SubtensorModule::on_idle(System::block_number(), Weight::MAX); @@ -3655,8 +3671,11 @@ fn test_arbitration_period_extension() { let another_coldkey = U256::from(4); let current_block = SubtensorModule::get_current_block_as_u64(); - let (work1, nonce1) = - generate_valid_pow(¤t_coldkey, current_block, U256::from(10_000_000u64)); + let (work1, nonce1) = generate_valid_pow( + ¤t_coldkey, + current_block, + U256::from(BaseDifficulty::::get()), + ); let (work2, nonce2) = generate_valid_pow(¤t_coldkey, current_block, U256::from(20_000_000u64)); SubtensorModule::add_balance_to_coldkey_account( @@ -3725,10 +3744,16 @@ fn test_concurrent_arbitrated_coldkey_swaps() { ); let current_block = SubtensorModule::get_current_block_as_u64(); - let (work1, nonce1) = - generate_valid_pow(&coldkey1, current_block, U256::from(10_000_000u64)); - let (work2, nonce2) = - generate_valid_pow(&coldkey2, current_block, U256::from(10_000_000u64)); + let (work1, nonce1) = generate_valid_pow( + &coldkey1, + current_block, + U256::from(BaseDifficulty::::get()), + ); + let (work2, nonce2) = generate_valid_pow( + &coldkey2, + current_block, + U256::from(BaseDifficulty::::get()), + ); // Schedule swaps for both coldkeys assert_ok!(SubtensorModule::do_schedule_coldkey_swap( &coldkey1.clone(), @@ -3773,7 +3798,7 @@ fn test_get_remaining_arbitration_period() { let (work, nonce) = generate_valid_pow( &coldkey_account_id, current_block, - U256::from(10_000_000u64), + U256::from(BaseDifficulty::::get()), ); SubtensorModule::add_balance_to_coldkey_account( @@ -3833,7 +3858,7 @@ fn test_transfer_coldkey_in_arbitration() { let (work, nonce) = generate_valid_pow( &coldkey_account_id, current_block, - U256::from(10_000_000u64), + U256::from(BaseDifficulty::::get()), ); // Schedule a coldkey swap to put the coldkey in arbitration @@ -3876,8 +3901,11 @@ fn test_add_stake_coldkey_in_arbitration() { ); let current_block = SubtensorModule::get_current_block_as_u64(); - let (work, nonce) = - generate_valid_pow(&coldkey_account_id, current_block, U256::from(1_000_000u64)); + let (work, nonce) = generate_valid_pow( + &coldkey_account_id, + current_block, + U256::from(BaseDifficulty::::get()), + ); // Schedule a coldkey swap to put the coldkey in arbitration assert_ok!(SubtensorModule::do_schedule_coldkey_swap( @@ -3916,8 +3944,11 @@ fn test_remove_stake_coldkey_in_arbitration() { SubtensorModule::increase_stake_on_hotkey_account(&hotkey_account_id, 1000); let current_block = SubtensorModule::get_current_block_as_u64(); - let (work, nonce) = - generate_valid_pow(&coldkey_account_id, current_block, U256::from(1_000_000u64)); + let (work, nonce) = generate_valid_pow( + &coldkey_account_id, + current_block, + U256::from(BaseDifficulty::::get()), + ); // Schedule a coldkey swap to put the coldkey in arbitration assert_ok!(SubtensorModule::do_schedule_coldkey_swap( @@ -3992,8 +4023,11 @@ fn test_coldkey_meets_enough() { add_network(netuid, 13, 0); register_ok_neuron(netuid, hotkey, coldkey, 0); let current_block = SubtensorModule::get_current_block_as_u64(); - let (work1, nonce1) = - generate_valid_pow(&coldkey, current_block, U256::from(10_000_000u64)); + let (work1, nonce1) = generate_valid_pow( + &coldkey, + current_block, + U256::from(BaseDifficulty::::get()), + ); assert_err!( SubtensorModule::do_schedule_coldkey_swap( &coldkey.clone(), @@ -4039,21 +4073,29 @@ fn test_comprehensive_coldkey_swap_scenarios() { SubnetOwner::::insert(netuid1, subnet_owner1); SubnetOwner::::insert(netuid2, subnet_owner2); - // Add balance to regular user + // Add balance to subnet owners and regular user + SubtensorModule::add_balance_to_coldkey_account( + &subnet_owner1, + MIN_BALANCE_TO_PERFORM_COLDKEY_SWAP, + ); + SubtensorModule::add_balance_to_coldkey_account( + &subnet_owner2, + MIN_BALANCE_TO_PERFORM_COLDKEY_SWAP, + ); SubtensorModule::add_balance_to_coldkey_account( ®ular_user, MIN_BALANCE_TO_PERFORM_COLDKEY_SWAP * 2, ); + // Set a very low base difficulty for testing + BaseDifficulty::::put(1); + let current_block = SubtensorModule::get_current_block_as_u64(); // Schedule swaps for subnet owners and regular user - let (work1, nonce1) = - generate_valid_pow(&subnet_owner1, current_block, U256::from(10_000_000u64)); - let (work2, nonce2) = - generate_valid_pow(&subnet_owner2, current_block, U256::from(20_000_000u64)); - let (work3, nonce3) = - generate_valid_pow(®ular_user, current_block, U256::from(30_000_000u64)); + let (work1, nonce1) = generate_valid_pow(&subnet_owner1, current_block, U256::from(BaseDifficulty::::get())); + let (work2, nonce2) = generate_valid_pow(&subnet_owner2, current_block, U256::from(2) * U256::from(BaseDifficulty::::get())); + let (work3, nonce3) = generate_valid_pow(®ular_user, current_block, U256::from(3)* U256::from(BaseDifficulty::::get()),); assert_ok!(SubtensorModule::do_schedule_coldkey_swap( &subnet_owner1, @@ -4110,7 +4152,7 @@ fn test_comprehensive_coldkey_swap_scenarios() { let (work4, nonce4) = generate_valid_pow( &subnet_owner1, current_block + i as u64, - U256::from(40_000_000u64), + U256::from(4) * U256::from(BaseDifficulty::::get()), ); assert_ok!(SubtensorModule::do_schedule_coldkey_swap( &subnet_owner1, @@ -4151,54 +4193,5 @@ fn test_comprehensive_coldkey_swap_scenarios() { MIN_BALANCE_TO_PERFORM_COLDKEY_SWAP * 2 ); assert_eq!(SubtensorModule::get_coldkey_balance(®ular_user), 0); - - // Test multiple calls for the same subnet owner - let (work5, nonce5) = - generate_valid_pow(&new_coldkey1, current_block + 7, U256::from(50_000_000u64)); - let (work6, nonce6) = - generate_valid_pow(&new_coldkey1, current_block + 7, U256::from(60_000_000u64)); - - assert_ok!(SubtensorModule::do_schedule_coldkey_swap( - &new_coldkey1, - &subnet_owner1, - work5.to_fixed_bytes().to_vec(), - current_block + 7, - nonce5 - )); - - assert_ok!(SubtensorModule::do_schedule_coldkey_swap( - &new_coldkey1, - &subnet_owner2, - work6.to_fixed_bytes().to_vec(), - current_block + 7, - nonce6 - )); - - assert_eq!( - ColdkeySwapDestinations::::get(new_coldkey1), - vec![subnet_owner1, subnet_owner2] - ); - - // Run through another arbitration period plus one block - for i in 0..6 { - next_block(); - SubtensorModule::on_idle(System::block_number(), Weight::MAX); - - log::info!( - "Block {}: Coldkey in arbitration: {}, Swap destinations: {:?}", - i + 7, - SubtensorModule::coldkey_in_arbitration(&new_coldkey1), - ColdkeySwapDestinations::::get(new_coldkey1) - ); - } - - // Check final state - log::info!( - "Final state - Swap destinations for new_coldkey1: {:?}", - ColdkeySwapDestinations::::get(new_coldkey1) - ); - assert!(ColdkeySwapDestinations::::get(new_coldkey1).is_empty()); - assert_eq!(SubnetOwner::::get(netuid1), subnet_owner1); - assert_eq!(SubnetOwner::::get(netuid2), subnet_owner2); }); } diff --git a/pallets/subtensor/tests/swap.rs b/pallets/subtensor/tests/swap.rs index c306089bf..90ebdfdcb 100644 --- a/pallets/subtensor/tests/swap.rs +++ b/pallets/subtensor/tests/swap.rs @@ -1368,11 +1368,9 @@ fn test_coldkey_has_associated_hotkeys() { add_network(netuid, 13, 0); register_ok_neuron(netuid, hotkey, coldkey, 0); SubtensorModule::add_balance_to_coldkey_account(&coldkey, 1000); - }); } - // SKIP_WASM_BUILD=1 RUST_LOG=info cargo test --test swap -- test_coldkey_swap_total --exact --nocapture #[test] fn test_coldkey_swap_total() { @@ -1408,95 +1406,263 @@ fn test_coldkey_swap_total() { register_ok_neuron(netuid1, delegate1, delegate1, 0); register_ok_neuron(netuid2, delegate2, delegate2, 0); register_ok_neuron(netuid3, delegate3, delegate3, 0); - assert_ok!(SubtensorModule::do_become_delegate(<::RuntimeOrigin>::signed(coldkey), hotkey1, u16::MAX / 10)); - assert_ok!(SubtensorModule::do_become_delegate(<::RuntimeOrigin>::signed(coldkey), hotkey2, u16::MAX / 10)); - assert_ok!(SubtensorModule::do_become_delegate(<::RuntimeOrigin>::signed(coldkey), hotkey3, u16::MAX / 10)); - assert_ok!(SubtensorModule::do_become_delegate(<::RuntimeOrigin>::signed(delegate1), delegate1, u16::MAX / 10)); - assert_ok!(SubtensorModule::do_become_delegate(<::RuntimeOrigin>::signed(delegate2), delegate2, u16::MAX / 10)); - assert_ok!(SubtensorModule::do_become_delegate(<::RuntimeOrigin>::signed(delegate3), delegate3, u16::MAX / 10)); - - assert_ok!(SubtensorModule::add_stake(<::RuntimeOrigin>::signed(coldkey), hotkey1, 100 )); - assert_ok!(SubtensorModule::add_stake(<::RuntimeOrigin>::signed(coldkey), hotkey2, 100 )); - assert_ok!(SubtensorModule::add_stake(<::RuntimeOrigin>::signed(coldkey), hotkey3, 100 )); - assert_ok!(SubtensorModule::add_stake(<::RuntimeOrigin>::signed(coldkey), delegate1, 100 )); - assert_ok!(SubtensorModule::add_stake(<::RuntimeOrigin>::signed(coldkey), delegate2, 100 )); - assert_ok!(SubtensorModule::add_stake(<::RuntimeOrigin>::signed(coldkey), delegate3, 100 )); - - assert_ok!(SubtensorModule::add_stake(<::RuntimeOrigin>::signed(delegate1), hotkey1, 100 )); - assert_ok!(SubtensorModule::add_stake(<::RuntimeOrigin>::signed(delegate2), hotkey2, 100 )); - assert_ok!(SubtensorModule::add_stake(<::RuntimeOrigin>::signed(delegate3), hotkey3, 100 )); - - assert_ok!(SubtensorModule::add_stake(<::RuntimeOrigin>::signed(delegate1), delegate1, 100 )); - assert_ok!(SubtensorModule::add_stake(<::RuntimeOrigin>::signed(delegate2), delegate2, 100 )); - assert_ok!(SubtensorModule::add_stake(<::RuntimeOrigin>::signed(delegate3), delegate3, 100 )); - - assert_ok!(SubtensorModule::add_stake(<::RuntimeOrigin>::signed(nominator1), hotkey1, 100 )); - assert_ok!(SubtensorModule::add_stake(<::RuntimeOrigin>::signed(nominator2), hotkey2, 100 )); - assert_ok!(SubtensorModule::add_stake(<::RuntimeOrigin>::signed(nominator3), hotkey3, 100 )); - - assert_ok!(SubtensorModule::add_stake(<::RuntimeOrigin>::signed(nominator1), delegate1, 100 )); - assert_ok!(SubtensorModule::add_stake(<::RuntimeOrigin>::signed(nominator2), delegate2, 100 )); - assert_ok!(SubtensorModule::add_stake(<::RuntimeOrigin>::signed(nominator3), delegate3, 100 )); - - assert_eq!( SubtensorModule::get_owned_hotkeys(&coldkey), vec![hotkey1, hotkey2, hotkey3] ); - assert_eq!( SubtensorModule::get_all_staked_hotkeys(&coldkey), vec![hotkey1, hotkey2, hotkey3, delegate1, delegate2, delegate3] ); - assert_eq!( SubtensorModule::get_total_stake_for_coldkey(&coldkey), 600 ); - assert_eq!( SubtensorModule::get_total_stake_for_hotkey(&hotkey1), 300 ); - assert_eq!( SubtensorModule::get_total_stake_for_hotkey(&hotkey2), 300 ); - assert_eq!( SubtensorModule::get_total_stake_for_hotkey(&hotkey3), 300 ); - assert_eq!( SubtensorModule::get_total_stake_for_hotkey(&delegate1), 300 ); - assert_eq!( SubtensorModule::get_total_stake_for_hotkey(&delegate2), 300 ); - assert_eq!( SubtensorModule::get_total_stake_for_hotkey(&delegate3), 300 ); - - - assert_eq!( SubtensorModule::get_owned_hotkeys(&delegate1), vec![delegate1] ); - assert_eq!( SubtensorModule::get_owned_hotkeys(&delegate2), vec![delegate2] ); - assert_eq!( SubtensorModule::get_owned_hotkeys(&delegate3), vec![delegate3] ); - assert_eq!( SubtensorModule::get_all_staked_hotkeys(&delegate1), vec![delegate1, hotkey1] ); - assert_eq!( SubtensorModule::get_all_staked_hotkeys(&delegate2), vec![delegate2, hotkey2] ); - assert_eq!( SubtensorModule::get_all_staked_hotkeys(&delegate3), vec![delegate3, hotkey3] ); - - assert_eq!( SubtensorModule::get_owned_hotkeys(&nominator1), vec![] ); - assert_eq!( SubtensorModule::get_owned_hotkeys(&nominator2), vec![] ); - assert_eq!( SubtensorModule::get_owned_hotkeys(&nominator3), vec![] ); - - assert_eq!( SubtensorModule::get_all_staked_hotkeys(&nominator1), vec![hotkey1, delegate1] ); - assert_eq!( SubtensorModule::get_all_staked_hotkeys(&nominator2), vec![hotkey2, delegate2] ); - assert_eq!( SubtensorModule::get_all_staked_hotkeys(&nominator3), vec![hotkey3, delegate3] ); + assert_ok!(SubtensorModule::do_become_delegate( + <::RuntimeOrigin>::signed(coldkey), + hotkey1, + u16::MAX / 10 + )); + assert_ok!(SubtensorModule::do_become_delegate( + <::RuntimeOrigin>::signed(coldkey), + hotkey2, + u16::MAX / 10 + )); + assert_ok!(SubtensorModule::do_become_delegate( + <::RuntimeOrigin>::signed(coldkey), + hotkey3, + u16::MAX / 10 + )); + assert_ok!(SubtensorModule::do_become_delegate( + <::RuntimeOrigin>::signed(delegate1), + delegate1, + u16::MAX / 10 + )); + assert_ok!(SubtensorModule::do_become_delegate( + <::RuntimeOrigin>::signed(delegate2), + delegate2, + u16::MAX / 10 + )); + assert_ok!(SubtensorModule::do_become_delegate( + <::RuntimeOrigin>::signed(delegate3), + delegate3, + u16::MAX / 10 + )); + + assert_ok!(SubtensorModule::add_stake( + <::RuntimeOrigin>::signed(coldkey), + hotkey1, + 100 + )); + assert_ok!(SubtensorModule::add_stake( + <::RuntimeOrigin>::signed(coldkey), + hotkey2, + 100 + )); + assert_ok!(SubtensorModule::add_stake( + <::RuntimeOrigin>::signed(coldkey), + hotkey3, + 100 + )); + assert_ok!(SubtensorModule::add_stake( + <::RuntimeOrigin>::signed(coldkey), + delegate1, + 100 + )); + assert_ok!(SubtensorModule::add_stake( + <::RuntimeOrigin>::signed(coldkey), + delegate2, + 100 + )); + assert_ok!(SubtensorModule::add_stake( + <::RuntimeOrigin>::signed(coldkey), + delegate3, + 100 + )); + + assert_ok!(SubtensorModule::add_stake( + <::RuntimeOrigin>::signed(delegate1), + hotkey1, + 100 + )); + assert_ok!(SubtensorModule::add_stake( + <::RuntimeOrigin>::signed(delegate2), + hotkey2, + 100 + )); + assert_ok!(SubtensorModule::add_stake( + <::RuntimeOrigin>::signed(delegate3), + hotkey3, + 100 + )); + + assert_ok!(SubtensorModule::add_stake( + <::RuntimeOrigin>::signed(delegate1), + delegate1, + 100 + )); + assert_ok!(SubtensorModule::add_stake( + <::RuntimeOrigin>::signed(delegate2), + delegate2, + 100 + )); + assert_ok!(SubtensorModule::add_stake( + <::RuntimeOrigin>::signed(delegate3), + delegate3, + 100 + )); + + assert_ok!(SubtensorModule::add_stake( + <::RuntimeOrigin>::signed(nominator1), + hotkey1, + 100 + )); + assert_ok!(SubtensorModule::add_stake( + <::RuntimeOrigin>::signed(nominator2), + hotkey2, + 100 + )); + assert_ok!(SubtensorModule::add_stake( + <::RuntimeOrigin>::signed(nominator3), + hotkey3, + 100 + )); + + assert_ok!(SubtensorModule::add_stake( + <::RuntimeOrigin>::signed(nominator1), + delegate1, + 100 + )); + assert_ok!(SubtensorModule::add_stake( + <::RuntimeOrigin>::signed(nominator2), + delegate2, + 100 + )); + assert_ok!(SubtensorModule::add_stake( + <::RuntimeOrigin>::signed(nominator3), + delegate3, + 100 + )); + + assert_eq!( + SubtensorModule::get_owned_hotkeys(&coldkey), + vec![hotkey1, hotkey2, hotkey3] + ); + assert_eq!( + SubtensorModule::get_all_staked_hotkeys(&coldkey), + vec![hotkey1, hotkey2, hotkey3, delegate1, delegate2, delegate3] + ); + assert_eq!(SubtensorModule::get_total_stake_for_coldkey(&coldkey), 600); + assert_eq!(SubtensorModule::get_total_stake_for_hotkey(&hotkey1), 300); + assert_eq!(SubtensorModule::get_total_stake_for_hotkey(&hotkey2), 300); + assert_eq!(SubtensorModule::get_total_stake_for_hotkey(&hotkey3), 300); + assert_eq!(SubtensorModule::get_total_stake_for_hotkey(&delegate1), 300); + assert_eq!(SubtensorModule::get_total_stake_for_hotkey(&delegate2), 300); + assert_eq!(SubtensorModule::get_total_stake_for_hotkey(&delegate3), 300); + + assert_eq!( + SubtensorModule::get_owned_hotkeys(&delegate1), + vec![delegate1] + ); + assert_eq!( + SubtensorModule::get_owned_hotkeys(&delegate2), + vec![delegate2] + ); + assert_eq!( + SubtensorModule::get_owned_hotkeys(&delegate3), + vec![delegate3] + ); + assert_eq!( + SubtensorModule::get_all_staked_hotkeys(&delegate1), + vec![delegate1, hotkey1] + ); + assert_eq!( + SubtensorModule::get_all_staked_hotkeys(&delegate2), + vec![delegate2, hotkey2] + ); + assert_eq!( + SubtensorModule::get_all_staked_hotkeys(&delegate3), + vec![delegate3, hotkey3] + ); + + assert_eq!(SubtensorModule::get_owned_hotkeys(&nominator1), vec![]); + assert_eq!(SubtensorModule::get_owned_hotkeys(&nominator2), vec![]); + assert_eq!(SubtensorModule::get_owned_hotkeys(&nominator3), vec![]); + + assert_eq!( + SubtensorModule::get_all_staked_hotkeys(&nominator1), + vec![hotkey1, delegate1] + ); + assert_eq!( + SubtensorModule::get_all_staked_hotkeys(&nominator2), + vec![hotkey2, delegate2] + ); + assert_eq!( + SubtensorModule::get_all_staked_hotkeys(&nominator3), + vec![hotkey3, delegate3] + ); // Perform the swap let new_coldkey = U256::from(1100); - assert_eq!( SubtensorModule::get_total_stake_for_coldkey(&coldkey), 600 ); - assert_ok!(SubtensorModule::perform_swap_coldkey( &coldkey, &new_coldkey )); - assert_eq!( SubtensorModule::get_total_stake_for_coldkey(&new_coldkey), 600 ); + assert_eq!(SubtensorModule::get_total_stake_for_coldkey(&coldkey), 600); + assert_ok!(SubtensorModule::perform_swap_coldkey( + &coldkey, + &new_coldkey + )); + assert_eq!( + SubtensorModule::get_total_stake_for_coldkey(&new_coldkey), + 600 + ); // Check everything is swapped. - assert_eq!( SubtensorModule::get_owned_hotkeys(&new_coldkey), vec![hotkey1, hotkey2, hotkey3] ); - assert_eq!( SubtensorModule::get_all_staked_hotkeys(&new_coldkey), vec![hotkey1, hotkey2, hotkey3, delegate1, delegate2, delegate3] ); - assert_eq!( SubtensorModule::get_total_stake_for_coldkey(&new_coldkey), 600 ); - assert_eq!( SubtensorModule::get_total_stake_for_hotkey(&hotkey1), 300 ); - assert_eq!( SubtensorModule::get_total_stake_for_hotkey(&hotkey2), 300 ); - assert_eq!( SubtensorModule::get_total_stake_for_hotkey(&hotkey3), 300 ); - assert_eq!( SubtensorModule::get_total_stake_for_hotkey(&delegate1), 300 ); - assert_eq!( SubtensorModule::get_total_stake_for_hotkey(&delegate2), 300 ); - assert_eq!( SubtensorModule::get_total_stake_for_hotkey(&delegate3), 300 ); - - assert_eq!( SubtensorModule::get_owned_hotkeys(&delegate1), vec![delegate1] ); - assert_eq!( SubtensorModule::get_owned_hotkeys(&delegate2), vec![delegate2] ); - assert_eq!( SubtensorModule::get_owned_hotkeys(&delegate3), vec![delegate3] ); - assert_eq!( SubtensorModule::get_all_staked_hotkeys(&delegate1), vec![delegate1, hotkey1] ); - assert_eq!( SubtensorModule::get_all_staked_hotkeys(&delegate2), vec![delegate2, hotkey2] ); - assert_eq!( SubtensorModule::get_all_staked_hotkeys(&delegate3), vec![delegate3, hotkey3] ); - - assert_eq!( SubtensorModule::get_owned_hotkeys(&nominator1), vec![] ); - assert_eq!( SubtensorModule::get_owned_hotkeys(&nominator2), vec![] ); - assert_eq!( SubtensorModule::get_owned_hotkeys(&nominator3), vec![] ); - - assert_eq!( SubtensorModule::get_all_staked_hotkeys(&nominator1), vec![hotkey1, delegate1] ); - assert_eq!( SubtensorModule::get_all_staked_hotkeys(&nominator2), vec![hotkey2, delegate2] ); - assert_eq!( SubtensorModule::get_all_staked_hotkeys(&nominator3), vec![hotkey3, delegate3] ); + assert_eq!( + SubtensorModule::get_owned_hotkeys(&new_coldkey), + vec![hotkey1, hotkey2, hotkey3] + ); + assert_eq!( + SubtensorModule::get_all_staked_hotkeys(&new_coldkey), + vec![hotkey1, hotkey2, hotkey3, delegate1, delegate2, delegate3] + ); + assert_eq!( + SubtensorModule::get_total_stake_for_coldkey(&new_coldkey), + 600 + ); + assert_eq!(SubtensorModule::get_total_stake_for_hotkey(&hotkey1), 300); + assert_eq!(SubtensorModule::get_total_stake_for_hotkey(&hotkey2), 300); + assert_eq!(SubtensorModule::get_total_stake_for_hotkey(&hotkey3), 300); + assert_eq!(SubtensorModule::get_total_stake_for_hotkey(&delegate1), 300); + assert_eq!(SubtensorModule::get_total_stake_for_hotkey(&delegate2), 300); + assert_eq!(SubtensorModule::get_total_stake_for_hotkey(&delegate3), 300); + + assert_eq!( + SubtensorModule::get_owned_hotkeys(&delegate1), + vec![delegate1] + ); + assert_eq!( + SubtensorModule::get_owned_hotkeys(&delegate2), + vec![delegate2] + ); + assert_eq!( + SubtensorModule::get_owned_hotkeys(&delegate3), + vec![delegate3] + ); + assert_eq!( + SubtensorModule::get_all_staked_hotkeys(&delegate1), + vec![delegate1, hotkey1] + ); + assert_eq!( + SubtensorModule::get_all_staked_hotkeys(&delegate2), + vec![delegate2, hotkey2] + ); + assert_eq!( + SubtensorModule::get_all_staked_hotkeys(&delegate3), + vec![delegate3, hotkey3] + ); + assert_eq!(SubtensorModule::get_owned_hotkeys(&nominator1), vec![]); + assert_eq!(SubtensorModule::get_owned_hotkeys(&nominator2), vec![]); + assert_eq!(SubtensorModule::get_owned_hotkeys(&nominator3), vec![]); + assert_eq!( + SubtensorModule::get_all_staked_hotkeys(&nominator1), + vec![hotkey1, delegate1] + ); + assert_eq!( + SubtensorModule::get_all_staked_hotkeys(&nominator2), + vec![hotkey2, delegate2] + ); + assert_eq!( + SubtensorModule::get_all_staked_hotkeys(&nominator3), + vec![hotkey3, delegate3] + ); }); } diff --git a/runtime/src/lib.rs b/runtime/src/lib.rs index ea4dded94..9be039e6b 100644 --- a/runtime/src/lib.rs +++ b/runtime/src/lib.rs @@ -879,6 +879,7 @@ parameter_types! { pub const InitialAlphaHigh: u16 = 58982; // Represents 0.9 as per the production default pub const InitialAlphaLow: u16 = 45875; // Represents 0.7 as per the production default pub const InitialLiquidAlphaOn: bool = false; // Default value for LiquidAlphaOn + pub const SubtensorInitialBaseDifficulty: u64 = 10_000_000; // Base difficulty } impl pallet_subtensor::Config for Runtime { @@ -934,6 +935,7 @@ impl pallet_subtensor::Config for Runtime { type AlphaHigh = InitialAlphaHigh; type AlphaLow = InitialAlphaLow; type LiquidAlphaOn = InitialLiquidAlphaOn; + type InitialBaseDifficulty = SubtensorInitialBaseDifficulty; } use sp_runtime::BoundedVec; From a14337b07bc03c1281dcaa882b993c034f9c09d4 Mon Sep 17 00:00:00 2001 From: open-junius Date: Tue, 9 Jul 2024 19:00:15 +0800 Subject: [PATCH 42/51] make it easier --- pallets/subtensor/tests/staking.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pallets/subtensor/tests/staking.rs b/pallets/subtensor/tests/staking.rs index 9d3a4698b..17017ce97 100644 --- a/pallets/subtensor/tests/staking.rs +++ b/pallets/subtensor/tests/staking.rs @@ -4094,8 +4094,8 @@ fn test_comprehensive_coldkey_swap_scenarios() { // Schedule swaps for subnet owners and regular user let (work1, nonce1) = generate_valid_pow(&subnet_owner1, current_block, U256::from(BaseDifficulty::::get())); - let (work2, nonce2) = generate_valid_pow(&subnet_owner2, current_block, U256::from(2) * U256::from(BaseDifficulty::::get())); - let (work3, nonce3) = generate_valid_pow(®ular_user, current_block, U256::from(3)* U256::from(BaseDifficulty::::get()),); + let (work2, nonce2) = generate_valid_pow(&subnet_owner2, current_block, U256::from(BaseDifficulty::::get())); + let (work3, nonce3) = generate_valid_pow(®ular_user, current_block, U256::from(BaseDifficulty::::get())); assert_ok!(SubtensorModule::do_schedule_coldkey_swap( &subnet_owner1, From 83819ffe4bf70f71e8db6533d693f1db4b80b750 Mon Sep 17 00:00:00 2001 From: open-junius Date: Tue, 9 Jul 2024 19:02:33 +0800 Subject: [PATCH 43/51] fix fmt --- pallets/subtensor/src/lib.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/pallets/subtensor/src/lib.rs b/pallets/subtensor/src/lib.rs index 8eddc4937..787989f17 100644 --- a/pallets/subtensor/src/lib.rs +++ b/pallets/subtensor/src/lib.rs @@ -87,7 +87,6 @@ pub mod pallet { /// Minimum balance required to perform a coldkey swap pub const MIN_BALANCE_TO_PERFORM_COLDKEY_SWAP: u64 = 1_000_000_000; // 1 TAO in RAO - #[pallet::pallet] #[pallet::without_storage_info] #[pallet::storage_version(STORAGE_VERSION)] From 4563e782d23d751e13768c1fa5a9fa5f96b6ee8a Mon Sep 17 00:00:00 2001 From: Samuel Dare Date: Tue, 9 Jul 2024 15:15:57 +0400 Subject: [PATCH 44/51] feat: get total delegated stake --- pallets/subtensor/src/delegate_info.rs | 66 +++++++-- pallets/subtensor/tests/staking.rs | 196 +++++++++++++++++++++++++ 2 files changed, 246 insertions(+), 16 deletions(-) diff --git a/pallets/subtensor/src/delegate_info.rs b/pallets/subtensor/src/delegate_info.rs index 1f8b06b69..38f97bb8a 100644 --- a/pallets/subtensor/src/delegate_info.rs +++ b/pallets/subtensor/src/delegate_info.rs @@ -40,11 +40,7 @@ impl Pallet { let mut emissions_per_day: U64F64 = U64F64::from_num(0); for netuid in registrations.iter() { - let _uid = Self::get_uid_for_net_and_hotkey(*netuid, &delegate.clone()); - if _uid.is_err() { - continue; // this should never happen - } else { - let uid = _uid.expect("Delegate's UID should be ok"); + if let Ok(uid) = Self::get_uid_for_net_and_hotkey(*netuid, &delegate.clone()) { let validator_permit = Self::get_validator_permit_for_uid(*netuid, uid); if validator_permit { validator_permits.push((*netuid).into()); @@ -52,9 +48,11 @@ impl Pallet { let emission: U64F64 = Self::get_emission_for_uid(*netuid, uid).into(); let tempo: U64F64 = Self::get_tempo(*netuid).into(); - let epochs_per_day: U64F64 = U64F64::from_num(7200).saturating_div(tempo); - emissions_per_day = - emissions_per_day.saturating_add(emission.saturating_mul(epochs_per_day)); + if tempo > U64F64::from_num(0) { + let epochs_per_day: U64F64 = U64F64::from_num(7200).saturating_div(tempo); + emissions_per_day = + emissions_per_day.saturating_add(emission.saturating_mul(epochs_per_day)); + } } } @@ -63,15 +61,15 @@ impl Pallet { let total_stake: U64F64 = Self::get_total_stake_for_hotkey(&delegate.clone()).into(); - let mut return_per_1000: U64F64 = U64F64::from_num(0); - - if total_stake > U64F64::from_num(0) { - return_per_1000 = emissions_per_day + let return_per_1000: U64F64 = if total_stake > U64F64::from_num(0) { + emissions_per_day .saturating_mul(U64F64::from_num(0.82)) - .saturating_div(total_stake.saturating_div(U64F64::from_num(1000))); - } + .saturating_div(total_stake.saturating_div(U64F64::from_num(1000))) + } else { + U64F64::from_num(0) + }; - return DelegateInfo { + DelegateInfo { delegate_ss58: delegate.clone(), take, nominators, @@ -80,7 +78,7 @@ impl Pallet { validator_permits, return_per_1000: U64F64::to_num::(return_per_1000).into(), total_daily_return: U64F64::to_num::(emissions_per_day).into(), - }; + } } pub fn get_delegate(delegate_account_vec: Vec) -> Option> { @@ -132,4 +130,40 @@ impl Pallet { delegates } + + /// Returns the total delegated stake for a given delegate, excluding the stake from the delegate's owner. + /// + /// # Arguments + /// + /// * `delegate` - A reference to the account ID of the delegate. + /// + /// # Returns + /// + /// * `u64` - The total amount of stake delegated to the delegate, excluding the owner's stake. + /// + /// + /// # Notes + /// + /// This function retrieves the delegate's information and calculates the total stake from all nominators, + /// excluding the stake from the delegate's owner. + pub fn get_total_delegated_stake(delegate: &T::AccountId) -> u64 { + if !>::contains_key(delegate) { + return 0; + } + + // Retrieve the delegate's information + let delegate_info: DelegateInfo = + Self::get_delegate_by_existing_account(delegate.clone()); + + // Retrieve the owner's account ID for the given delegate + let owner: T::AccountId = Self::get_owning_coldkey_for_hotkey(delegate); + + // Calculate the total stake from all nominators, excluding the owner's stake + delegate_info + .nominators + .iter() + .filter(|(nominator, _)| nominator != &owner) // Exclude the owner's stake + .map(|(_, stake)| stake.0 as u64) // Map the stake to u64 + .sum() // Sum the stakes + } } diff --git a/pallets/subtensor/tests/staking.rs b/pallets/subtensor/tests/staking.rs index 17017ce97..39ed875a1 100644 --- a/pallets/subtensor/tests/staking.rs +++ b/pallets/subtensor/tests/staking.rs @@ -13,6 +13,7 @@ use frame_support::sp_runtime::DispatchError; use mock::*; use pallet_balances::Call as BalancesCall; use pallet_subtensor::*; +use serde::de; use sp_core::{H256, U256}; use sp_runtime::traits::SignedExtension; @@ -4195,3 +4196,198 @@ fn test_comprehensive_coldkey_swap_scenarios() { assert_eq!(SubtensorModule::get_coldkey_balance(®ular_user), 0); }); } + +#[test] +fn test_get_total_delegated_stake_after_unstaking() { + new_test_ext(1).execute_with(|| { + let delegate = U256::from(1); + let coldkey = U256::from(2); + let delegator = U256::from(3); + let initial_stake = 2000; + let unstake_amount = 500; + let netuid = 1u16; + let existential_deposit = 1; // Account for the existential deposit + + add_network(netuid, 0, 0); + + register_ok_neuron(netuid, delegate, coldkey, 0); + + // Make the delegate a delegate + assert_ok!(SubtensorModule::become_delegate( + RuntimeOrigin::signed(coldkey), + delegate + )); + + // Add balance to delegator + SubtensorModule::add_balance_to_coldkey_account(&delegator, initial_stake); + + // Delegate stake + assert_ok!(SubtensorModule::add_stake( + RuntimeOrigin::signed(delegator), + delegate, + initial_stake + )); + + // Unstake part of the delegation + assert_ok!(SubtensorModule::remove_stake( + RuntimeOrigin::signed(delegator), + delegate, + unstake_amount + )); + + // Calculate the expected delegated stake + let expected_delegated_stake = initial_stake - unstake_amount - existential_deposit; + + // Check the total delegated stake after unstaking + assert_eq!( + SubtensorModule::get_total_delegated_stake(&delegate), + expected_delegated_stake + ); + }); +} + +#[test] +fn test_get_total_delegated_stake_no_delegations() { + new_test_ext(1).execute_with(|| { + let delegate = U256::from(1); + let coldkey = U256::from(2); + let netuid = 1u16; + + add_network(netuid, 0, 0); + register_ok_neuron(netuid, delegate, coldkey, 0); + + // Make the delegate a delegate + assert_ok!(SubtensorModule::become_delegate( + RuntimeOrigin::signed(coldkey), + delegate + )); + + // Check that there's no delegated stake + assert_eq!(SubtensorModule::get_total_delegated_stake(&delegate), 0); + }); +} + +#[test] +fn test_get_total_delegated_stake_single_delegator() { + new_test_ext(1).execute_with(|| { + let delegate = U256::from(1); + let coldkey = U256::from(2); + let delegator = U256::from(3); + let stake_amount = 1000; + let netuid = 1u16; + + add_network(netuid, 0, 0); + register_ok_neuron(netuid, delegate, coldkey, 0); + + // Make the delegate a delegate + assert_ok!(SubtensorModule::become_delegate( + RuntimeOrigin::signed(coldkey), + delegate + )); + + // Add balance to delegator + SubtensorModule::add_balance_to_coldkey_account(&delegator, stake_amount); + + // Delegate stake + assert_ok!(SubtensorModule::add_stake( + RuntimeOrigin::signed(delegator), + delegate, + stake_amount + )); + + // Check the total delegated stake + assert_eq!( + SubtensorModule::get_total_delegated_stake(&delegate), + stake_amount - 1 // Subtract 1 for existential deposit + ); + }); +} + +#[test] +fn test_get_total_delegated_stake_multiple_delegators() { + new_test_ext(1).execute_with(|| { + let delegate = U256::from(1); + let coldkey = U256::from(2); + let delegator1 = U256::from(3); + let delegator2 = U256::from(4); + let stake_amount1 = 1000; + let stake_amount2 = 2000; + let netuid = 1u16; + + add_network(netuid, 0, 0); + register_ok_neuron(netuid, delegate, coldkey, 0); + + // Make the delegate a delegate + assert_ok!(SubtensorModule::become_delegate( + RuntimeOrigin::signed(coldkey), + delegate + )); + + // Add balance to delegators + SubtensorModule::add_balance_to_coldkey_account(&delegator1, stake_amount1); + SubtensorModule::add_balance_to_coldkey_account(&delegator2, stake_amount2); + + // Delegate stakes + assert_ok!(SubtensorModule::add_stake( + RuntimeOrigin::signed(delegator1), + delegate, + stake_amount1 + )); + assert_ok!(SubtensorModule::add_stake( + RuntimeOrigin::signed(delegator2), + delegate, + stake_amount2 + )); + + // Check the total delegated stake + assert_eq!( + SubtensorModule::get_total_delegated_stake(&delegate), + stake_amount1 + stake_amount2 - 2 // Subtract 2 for existential deposits + ); + }); +} + +#[test] +fn test_get_total_delegated_stake_exclude_owner_stake() { + new_test_ext(1).execute_with(|| { + let delegate = U256::from(1); + let coldkey = U256::from(2); + let delegator = U256::from(3); + let owner_stake = 5000; + let delegator_stake = 1000; + let netuid = 1u16; + + add_network(netuid, 0, 0); + register_ok_neuron(netuid, delegate, coldkey, 0); + + // Make the delegate a delegate + assert_ok!(SubtensorModule::become_delegate( + RuntimeOrigin::signed(coldkey), + delegate + )); + + // Add balance to owner and delegator + SubtensorModule::add_balance_to_coldkey_account(&coldkey, owner_stake); + SubtensorModule::add_balance_to_coldkey_account(&delegator, delegator_stake); + + // Owner adds stake + assert_ok!(SubtensorModule::add_stake( + RuntimeOrigin::signed(coldkey), + delegate, + owner_stake + )); + + // Delegator adds stake + assert_ok!(SubtensorModule::add_stake( + RuntimeOrigin::signed(delegator), + delegate, + delegator_stake + )); + + // Check the total delegated stake (should exclude owner's stake) + assert_eq!( + SubtensorModule::get_total_delegated_stake(&delegate), + delegator_stake - 1 // Subtract 1 for existential deposit + ); + }); +} From 4eefcf9f093cb8584e86373f22de1e11242164d4 Mon Sep 17 00:00:00 2001 From: Samuel Dare Date: Tue, 9 Jul 2024 16:21:17 +0400 Subject: [PATCH 45/51] fix: delegated stake tests, whitelist serve_axon, lints --- pallets/admin-utils/tests/mock.rs | 2 + pallets/subtensor/src/delegate_info.rs | 67 ++-- pallets/subtensor/src/swap.rs | 13 +- pallets/subtensor/tests/staking.rs | 465 +++++++++++++++++++++---- runtime/src/lib.rs | 1 + 5 files changed, 445 insertions(+), 103 deletions(-) diff --git a/pallets/admin-utils/tests/mock.rs b/pallets/admin-utils/tests/mock.rs index a78eb6d3d..5f71626ad 100644 --- a/pallets/admin-utils/tests/mock.rs +++ b/pallets/admin-utils/tests/mock.rs @@ -114,6 +114,7 @@ parameter_types! { pub const InitialAlphaHigh: u16 = 58982; // Represents 0.9 as per the production default pub const InitialAlphaLow: u16 = 45875; // Represents 0.7 as per the production default pub const InitialLiquidAlphaOn: bool = false; // Default value for LiquidAlphaOn + pub const InitialBaseDifficulty: u64 = 10_000; // Base difficulty } impl pallet_subtensor::Config for Test { @@ -169,6 +170,7 @@ impl pallet_subtensor::Config for Test { type AlphaHigh = InitialAlphaHigh; type AlphaLow = InitialAlphaLow; type LiquidAlphaOn = InitialLiquidAlphaOn; + type InitialBaseDifficulty = InitialBaseDifficulty; } #[derive_impl(frame_system::config_preludes::TestDefaultConfig)] diff --git a/pallets/subtensor/src/delegate_info.rs b/pallets/subtensor/src/delegate_info.rs index 38f97bb8a..7ae50355a 100644 --- a/pallets/subtensor/src/delegate_info.rs +++ b/pallets/subtensor/src/delegate_info.rs @@ -131,39 +131,46 @@ impl Pallet { delegates } - /// Returns the total delegated stake for a given delegate, excluding the stake from the delegate's owner. - /// - /// # Arguments - /// - /// * `delegate` - A reference to the account ID of the delegate. - /// - /// # Returns - /// - /// * `u64` - The total amount of stake delegated to the delegate, excluding the owner's stake. - /// - /// - /// # Notes - /// - /// This function retrieves the delegate's information and calculates the total stake from all nominators, - /// excluding the stake from the delegate's owner. - pub fn get_total_delegated_stake(delegate: &T::AccountId) -> u64 { - if !>::contains_key(delegate) { - return 0; + pub fn get_total_delegated_stake(coldkey: &T::AccountId) -> u64 { + let mut total_delegated = 0u64; + + // Get all hotkeys associated with this coldkey + let hotkeys = StakingHotkeys::::get(coldkey); + + for hotkey in hotkeys { + let owner = Owner::::get(&hotkey); + + for (delegator, stake) in Stake::::iter_prefix(&hotkey) { + if delegator != owner { + total_delegated += stake; + } + } } - // Retrieve the delegate's information - let delegate_info: DelegateInfo = - Self::get_delegate_by_existing_account(delegate.clone()); + log::info!( + "Total delegated stake for coldkey {:?}: {}", + coldkey, + total_delegated + ); + total_delegated + } + + // Helper function to get total delegated stake for a hotkey + pub fn get_total_hotkey_delegated_stake(hotkey: &T::AccountId) -> u64 { + let mut total_delegated = 0u64; - // Retrieve the owner's account ID for the given delegate - let owner: T::AccountId = Self::get_owning_coldkey_for_hotkey(delegate); + // Iterate through all delegators for this hotkey + for (delegator, stake) in Stake::::iter_prefix(hotkey) { + if delegator != Self::get_coldkey_for_hotkey(hotkey) { + total_delegated += stake; + } + } + + total_delegated + } - // Calculate the total stake from all nominators, excluding the owner's stake - delegate_info - .nominators - .iter() - .filter(|(nominator, _)| nominator != &owner) // Exclude the owner's stake - .map(|(_, stake)| stake.0 as u64) // Map the stake to u64 - .sum() // Sum the stakes + // Helper function to get the coldkey associated with a hotkey + pub fn get_coldkey_for_hotkey(hotkey: &T::AccountId) -> T::AccountId { + Owner::::get(hotkey) } } diff --git a/pallets/subtensor/src/swap.rs b/pallets/subtensor/src/swap.rs index 1a272f216..7a548f289 100644 --- a/pallets/subtensor/src/swap.rs +++ b/pallets/subtensor/src/swap.rs @@ -203,9 +203,9 @@ impl Pallet { let all_staked_keys: Vec = StakingHotkeys::::get(coldkey); let mut total_staking_balance: u64 = 0; for hotkey in all_staked_keys { - total_staking_balance += Self::get_stake_for_coldkey_and_hotkey(&coldkey, &hotkey); + total_staking_balance += Self::get_stake_for_coldkey_and_hotkey(coldkey, &hotkey); } - total_staking_balance += Self::get_coldkey_balance(&coldkey); + total_staking_balance += Self::get_coldkey_balance(coldkey); total_staking_balance >= MIN_BALANCE_TO_PERFORM_COLDKEY_SWAP } @@ -250,10 +250,15 @@ impl Pallet { let is_subnet_owner = (0..=TotalNetworks::::get()) .any(|netuid| SubnetOwner::::get(netuid) == *old_coldkey); + // Check if the old_coldkey has more than 500 TAO delegated + let total_delegated = Self::get_total_delegated_stake(old_coldkey); + let has_sufficient_delegation = total_delegated > 500_000_000_000; // 500 TAO in RAO + // Only check the minimum balance if the old_coldkey is not a subnet owner - if !is_subnet_owner { + // and doesn't have sufficient delegation + if !(is_subnet_owner || has_sufficient_delegation) { ensure!( - Self::meets_min_allowed_coldkey_balance(&old_coldkey), + Self::meets_min_allowed_coldkey_balance(old_coldkey), Error::::InsufficientBalanceToPerformColdkeySwap ); } diff --git a/pallets/subtensor/tests/staking.rs b/pallets/subtensor/tests/staking.rs index 39ed875a1..873c81309 100644 --- a/pallets/subtensor/tests/staking.rs +++ b/pallets/subtensor/tests/staking.rs @@ -13,7 +13,6 @@ use frame_support::sp_runtime::DispatchError; use mock::*; use pallet_balances::Call as BalancesCall; use pallet_subtensor::*; -use serde::de; use sp_core::{H256, U256}; use sp_runtime::traits::SignedExtension; @@ -4200,22 +4199,21 @@ fn test_comprehensive_coldkey_swap_scenarios() { #[test] fn test_get_total_delegated_stake_after_unstaking() { new_test_ext(1).execute_with(|| { - let delegate = U256::from(1); - let coldkey = U256::from(2); + let netuid = 1u16; + let delegate_coldkey = U256::from(1); + let delegate_hotkey = U256::from(2); let delegator = U256::from(3); let initial_stake = 2000; let unstake_amount = 500; - let netuid = 1u16; let existential_deposit = 1; // Account for the existential deposit add_network(netuid, 0, 0); + register_ok_neuron(netuid, delegate_hotkey, delegate_coldkey, 0); - register_ok_neuron(netuid, delegate, coldkey, 0); - - // Make the delegate a delegate + // Make the account a delegate assert_ok!(SubtensorModule::become_delegate( - RuntimeOrigin::signed(coldkey), - delegate + RuntimeOrigin::signed(delegate_coldkey), + delegate_hotkey )); // Add balance to delegator @@ -4224,24 +4222,42 @@ fn test_get_total_delegated_stake_after_unstaking() { // Delegate stake assert_ok!(SubtensorModule::add_stake( RuntimeOrigin::signed(delegator), - delegate, + delegate_hotkey, initial_stake )); + // Check initial delegated stake + assert_eq!( + SubtensorModule::get_total_delegated_stake(&delegate_coldkey), + initial_stake - existential_deposit, + "Initial delegated stake is incorrect" + ); + // Unstake part of the delegation assert_ok!(SubtensorModule::remove_stake( RuntimeOrigin::signed(delegator), - delegate, + delegate_hotkey, unstake_amount )); // Calculate the expected delegated stake let expected_delegated_stake = initial_stake - unstake_amount - existential_deposit; + // Debug prints + println!("Initial stake: {}", initial_stake); + println!("Unstake amount: {}", unstake_amount); + println!("Existential deposit: {}", existential_deposit); + println!("Expected delegated stake: {}", expected_delegated_stake); + println!( + "Actual delegated stake: {}", + SubtensorModule::get_total_delegated_stake(&delegate_coldkey) + ); + // Check the total delegated stake after unstaking assert_eq!( - SubtensorModule::get_total_delegated_stake(&delegate), - expected_delegated_stake + SubtensorModule::get_total_delegated_stake(&delegate_coldkey), + expected_delegated_stake, + "Delegated stake mismatch after unstaking" ); }); } @@ -4270,35 +4286,49 @@ fn test_get_total_delegated_stake_no_delegations() { #[test] fn test_get_total_delegated_stake_single_delegator() { new_test_ext(1).execute_with(|| { - let delegate = U256::from(1); - let coldkey = U256::from(2); - let delegator = U256::from(3); - let stake_amount = 1000; let netuid = 1u16; + let delegate_coldkey = U256::from(1); + let delegate_hotkey = U256::from(2); + let delegator = U256::from(3); + let stake_amount = 999; + let existential_deposit = 1; // Account for the existential deposit add_network(netuid, 0, 0); - register_ok_neuron(netuid, delegate, coldkey, 0); + register_ok_neuron(netuid, delegate_hotkey, delegate_coldkey, 0); - // Make the delegate a delegate + // Make the account a delegate assert_ok!(SubtensorModule::become_delegate( - RuntimeOrigin::signed(coldkey), - delegate + RuntimeOrigin::signed(delegate_coldkey), + delegate_hotkey )); - // Add balance to delegator + // Add stake from delegator SubtensorModule::add_balance_to_coldkey_account(&delegator, stake_amount); - - // Delegate stake assert_ok!(SubtensorModule::add_stake( RuntimeOrigin::signed(delegator), - delegate, + delegate_hotkey, stake_amount )); - // Check the total delegated stake + // Debug prints + println!("Delegate coldkey: {:?}", delegate_coldkey); + println!("Delegate hotkey: {:?}", delegate_hotkey); + println!("Delegator: {:?}", delegator); + println!("Stake amount: {}", stake_amount); + println!("Existential deposit: {}", existential_deposit); + println!("Total stake for hotkey: {}", SubtensorModule::get_total_stake_for_hotkey(&delegate_hotkey)); + println!("Delegated stake for coldkey: {}", SubtensorModule::get_total_delegated_stake(&delegate_coldkey)); + + // Calculate expected delegated stake + let expected_delegated_stake = stake_amount - existential_deposit; + let actual_delegated_stake = SubtensorModule::get_total_delegated_stake(&delegate_coldkey); + assert_eq!( - SubtensorModule::get_total_delegated_stake(&delegate), - stake_amount - 1 // Subtract 1 for existential deposit + actual_delegated_stake, + expected_delegated_stake, + "Total delegated stake should match the delegator's stake minus existential deposit. Expected: {}, Actual: {}", + expected_delegated_stake, + actual_delegated_stake ); }); } @@ -4306,43 +4336,57 @@ fn test_get_total_delegated_stake_single_delegator() { #[test] fn test_get_total_delegated_stake_multiple_delegators() { new_test_ext(1).execute_with(|| { - let delegate = U256::from(1); - let coldkey = U256::from(2); + let netuid = 1u16; + let delegate_coldkey = U256::from(1); + let delegate_hotkey = U256::from(2); let delegator1 = U256::from(3); let delegator2 = U256::from(4); - let stake_amount1 = 1000; - let stake_amount2 = 2000; - let netuid = 1u16; + let stake1 = 1000; + let stake2 = 1999; + let existential_deposit = 1; // Account for the existential deposit add_network(netuid, 0, 0); - register_ok_neuron(netuid, delegate, coldkey, 0); + register_ok_neuron(netuid, delegate_hotkey, delegate_coldkey, 0); - // Make the delegate a delegate + // Make the account a delegate assert_ok!(SubtensorModule::become_delegate( - RuntimeOrigin::signed(coldkey), - delegate + RuntimeOrigin::signed(delegate_coldkey), + delegate_hotkey )); - // Add balance to delegators - SubtensorModule::add_balance_to_coldkey_account(&delegator1, stake_amount1); - SubtensorModule::add_balance_to_coldkey_account(&delegator2, stake_amount2); - - // Delegate stakes + // Add stake from delegator1 + SubtensorModule::add_balance_to_coldkey_account(&delegator1, stake1); assert_ok!(SubtensorModule::add_stake( RuntimeOrigin::signed(delegator1), - delegate, - stake_amount1 + delegate_hotkey, + stake1 )); + + // Add stake from delegator2 + SubtensorModule::add_balance_to_coldkey_account(&delegator2, stake2); assert_ok!(SubtensorModule::add_stake( RuntimeOrigin::signed(delegator2), - delegate, - stake_amount2 + delegate_hotkey, + stake2 )); - // Check the total delegated stake + // Debug prints + println!("Delegator1 stake: {}", stake1); + println!("Delegator2 stake: {}", stake2); + println!("Existential deposit: {}", existential_deposit); + println!("Total stake for hotkey: {}", SubtensorModule::get_total_stake_for_hotkey(&delegate_hotkey)); + println!("Delegated stake for coldkey: {}", SubtensorModule::get_total_delegated_stake(&delegate_coldkey)); + + // Calculate expected total delegated stake + let expected_total_delegated = stake1 + stake2 - 2 * existential_deposit; + let actual_total_delegated = SubtensorModule::get_total_delegated_stake(&delegate_coldkey); + assert_eq!( - SubtensorModule::get_total_delegated_stake(&delegate), - stake_amount1 + stake_amount2 - 2 // Subtract 2 for existential deposits + actual_total_delegated, + expected_total_delegated, + "Total delegated stake should match the sum of delegators' stakes minus existential deposits. Expected: {}, Actual: {}", + expected_total_delegated, + actual_total_delegated ); }); } @@ -4350,44 +4394,327 @@ fn test_get_total_delegated_stake_multiple_delegators() { #[test] fn test_get_total_delegated_stake_exclude_owner_stake() { new_test_ext(1).execute_with(|| { - let delegate = U256::from(1); - let coldkey = U256::from(2); - let delegator = U256::from(3); - let owner_stake = 5000; - let delegator_stake = 1000; let netuid = 1u16; + let delegate_coldkey = U256::from(1); + let delegate_hotkey = U256::from(2); + let delegator = U256::from(3); + let owner_stake = 1000; + let delegator_stake = 999; + let existential_deposit = 1; // Account for the existential deposit add_network(netuid, 0, 0); - register_ok_neuron(netuid, delegate, coldkey, 0); + register_ok_neuron(netuid, delegate_hotkey, delegate_coldkey, 0); - // Make the delegate a delegate + // Make the account a delegate assert_ok!(SubtensorModule::become_delegate( - RuntimeOrigin::signed(coldkey), - delegate + RuntimeOrigin::signed(delegate_coldkey), + delegate_hotkey )); - // Add balance to owner and delegator - SubtensorModule::add_balance_to_coldkey_account(&coldkey, owner_stake); - SubtensorModule::add_balance_to_coldkey_account(&delegator, delegator_stake); - - // Owner adds stake + // Add owner stake + SubtensorModule::add_balance_to_coldkey_account(&delegate_coldkey, owner_stake); assert_ok!(SubtensorModule::add_stake( - RuntimeOrigin::signed(coldkey), - delegate, + RuntimeOrigin::signed(delegate_coldkey), + delegate_hotkey, owner_stake )); - // Delegator adds stake + // Add delegator stake + SubtensorModule::add_balance_to_coldkey_account(&delegator, delegator_stake); assert_ok!(SubtensorModule::add_stake( RuntimeOrigin::signed(delegator), - delegate, + delegate_hotkey, delegator_stake )); + // Debug prints + println!("Owner stake: {}", owner_stake); + println!("Delegator stake: {}", delegator_stake); + println!("Existential deposit: {}", existential_deposit); + println!( + "Total stake for hotkey: {}", + SubtensorModule::get_total_stake_for_hotkey(&delegate_hotkey) + ); + println!( + "Delegated stake for coldkey: {}", + SubtensorModule::get_total_delegated_stake(&delegate_coldkey) + ); + // Check the total delegated stake (should exclude owner's stake) + let expected_delegated_stake = delegator_stake - existential_deposit; + let actual_delegated_stake = SubtensorModule::get_total_delegated_stake(&delegate_coldkey); + assert_eq!( - SubtensorModule::get_total_delegated_stake(&delegate), - delegator_stake - 1 // Subtract 1 for existential deposit + actual_delegated_stake, expected_delegated_stake, + "Delegated stake should exclude owner's stake. Expected: {}, Actual: {}", + expected_delegated_stake, actual_delegated_stake + ); + }); +} + +#[test] +fn test_do_schedule_coldkey_swap_subnet_owner_skips_min_balance() { + new_test_ext(1).execute_with(|| { + let netuid = 1u16; + let subnet_owner = U256::from(1); + let new_coldkey = U256::from(2); + let hotkey = U256::from(3); + let current_block = 0u64; + + add_network(netuid, 0, 0); + register_ok_neuron(netuid, hotkey, subnet_owner, 0); + + // Make subnet_owner the owner of the subnet + SubnetOwner::::insert(netuid, subnet_owner); + + // Ensure subnet_owner has less than minimum balance + assert!( + SubtensorModule::get_coldkey_balance(&subnet_owner) + < MIN_BALANCE_TO_PERFORM_COLDKEY_SWAP + ); + + // Generate valid PoW + let difficulty = U256::from(4) * U256::from(BaseDifficulty::::get()); + let (work, nonce) = generate_valid_pow(&subnet_owner, current_block, difficulty); + + // Debug prints + println!("Subnet owner: {:?}", subnet_owner); + println!("New coldkey: {:?}", new_coldkey); + println!("Current block: {}", current_block); + println!("Difficulty: {:?}", difficulty); + println!("Work: {:?}", work); + println!("Nonce: {}", nonce); + + // Verify the PoW + let seal = SubtensorModule::create_seal_hash(current_block, nonce, &subnet_owner); + println!("Calculated seal: {:?}", seal); + println!("Work matches seal: {}", work == seal); + println!( + "Seal meets difficulty: {}", + SubtensorModule::hash_meets_difficulty(&seal, difficulty) + ); + + // Attempt to schedule coldkey swap + let result = SubtensorModule::do_schedule_coldkey_swap( + &subnet_owner, + &new_coldkey, + work.to_fixed_bytes().to_vec(), + current_block, + nonce, + ); + + // Print the result + println!("Swap result: {:?}", result); + + assert_ok!(result); + + // Verify that the swap was scheduled + assert_eq!( + ColdkeySwapDestinations::::get(subnet_owner), + vec![new_coldkey] + ); + }); +} + +#[test] +fn test_do_schedule_coldkey_swap_delegate_with_500_tao_skips_min_balance() { + new_test_ext(1).execute_with(|| { + let netuid = 1u16; + let delegate_coldkey = U256::from(1); + let delegate_hotkey = U256::from(2); + let new_coldkey = U256::from(3); + let delegator = U256::from(4); + let current_block = 0u64; + + add_network(netuid, 0, 0); + register_ok_neuron(netuid, delegate_hotkey, delegate_coldkey, 0); + + // Make delegate a delegate + assert_ok!(SubtensorModule::become_delegate( + RuntimeOrigin::signed(delegate_coldkey), + delegate_hotkey + )); + + // Add more than 500 TAO of stake to the delegate's hotkey + let stake_amount = 501_000_000_000; // 501 TAO in RAO + SubtensorModule::add_balance_to_coldkey_account(&delegator, stake_amount); + assert_ok!(SubtensorModule::add_stake( + RuntimeOrigin::signed(delegator), + delegate_hotkey, + stake_amount + )); + + // Debug prints + println!( + "Delegator balance: {}", + SubtensorModule::get_coldkey_balance(&delegator) + ); + println!( + "Delegate coldkey balance: {}", + SubtensorModule::get_coldkey_balance(&delegate_coldkey) + ); + println!("Stake amount: {}", stake_amount); + println!( + "Delegate hotkey total stake: {}", + SubtensorModule::get_total_stake_for_hotkey(&delegate_hotkey) + ); + println!( + "Delegate coldkey delegated stake: {}", + SubtensorModule::get_total_delegated_stake(&delegate_coldkey) + ); + + // Ensure delegate's coldkey has less than minimum balance + assert!( + SubtensorModule::get_coldkey_balance(&delegate_coldkey) + < MIN_BALANCE_TO_PERFORM_COLDKEY_SWAP, + "Delegate coldkey balance should be less than minimum required" + ); + + // Ensure the delegate's hotkey has more than 500 TAO delegated + assert!( + SubtensorModule::get_total_delegated_stake(&delegate_coldkey) >= 500_000_000_000, + "Delegate hotkey should have at least 500 TAO delegated" + ); + + // Generate valid PoW + let (work, nonce) = generate_valid_pow( + &delegate_coldkey, + current_block, + U256::from(4) * U256::from(BaseDifficulty::::get()), + ); + + // Debug prints + println!("Work: {:?}", work); + println!("Nonce: {}", nonce); + + // Attempt to schedule coldkey swap + let result = SubtensorModule::do_schedule_coldkey_swap( + &delegate_coldkey, + &new_coldkey, + work.to_fixed_bytes().to_vec(), + current_block, + nonce, + ); + + // Print the result + println!("Swap result: {:?}", result); + + assert_ok!(result); + + // Verify that the swap was scheduled + assert_eq!( + ColdkeySwapDestinations::::get(delegate_coldkey), + vec![new_coldkey] + ); + + // Additional debug prints after swap + println!( + "Coldkey swap destinations: {:?}", + ColdkeySwapDestinations::::get(delegate_coldkey) + ); + println!( + "Is coldkey in arbitration: {}", + SubtensorModule::coldkey_in_arbitration(&delegate_coldkey) + ); + }); +} + +#[test] +fn test_do_schedule_coldkey_swap_regular_user_fails_min_balance() { + new_test_ext(1).execute_with(|| { + let netuid = 1u16; + let regular_user = U256::from(1); + let new_coldkey = U256::from(2); + let hotkey = U256::from(3); + let current_block = 0u64; + let nonce = 0u64; + + add_network(netuid, 0, 0); + register_ok_neuron(netuid, hotkey, regular_user, 0); + + // Ensure regular_user has less than minimum balance + assert!( + SubtensorModule::get_coldkey_balance(®ular_user) + < MIN_BALANCE_TO_PERFORM_COLDKEY_SWAP + ); + + let (work, _) = generate_valid_pow( + ®ular_user, + current_block, + U256::from(4) * U256::from(BaseDifficulty::::get()), + ); + + // Attempt to schedule coldkey swap + assert_noop!( + SubtensorModule::do_schedule_coldkey_swap( + ®ular_user, + &new_coldkey, + work.to_fixed_bytes().to_vec(), + current_block, + nonce + ), + Error::::InsufficientBalanceToPerformColdkeySwap + ); + + // Verify that the swap was not scheduled + assert!(ColdkeySwapDestinations::::get(regular_user).is_empty()); + }); +} + +#[test] +fn test_do_schedule_coldkey_swap_regular_user_passes_min_balance() { + new_test_ext(1).execute_with(|| { + let netuid = 1u16; + let regular_user = U256::from(1); + let new_coldkey = U256::from(2); + let hotkey = U256::from(3); + let current_block = 0u64; + + add_network(netuid, 0, 0); + register_ok_neuron(netuid, hotkey, regular_user, 0); + + // Ensure regular_user has more than minimum balance + SubtensorModule::add_balance_to_coldkey_account( + ®ular_user, + MIN_BALANCE_TO_PERFORM_COLDKEY_SWAP + 1, + ); + assert!( + SubtensorModule::get_coldkey_balance(®ular_user) + > MIN_BALANCE_TO_PERFORM_COLDKEY_SWAP + ); + + // Generate valid PoW + let (work, nonce) = generate_valid_pow( + ®ular_user, + current_block, + U256::from(4) * U256::from(BaseDifficulty::::get()), + ); + + // Debug prints + println!("Regular user: {:?}", regular_user); + println!("New coldkey: {:?}", new_coldkey); + println!("Current block: {}", current_block); + println!("Work: {:?}", work); + println!("Nonce: {}", nonce); + + // Attempt to schedule coldkey swap + let result = SubtensorModule::do_schedule_coldkey_swap( + ®ular_user, + &new_coldkey, + work.to_fixed_bytes().to_vec(), + current_block, + nonce, + ); + + // Print the result + println!("Swap result: {:?}", result); + + assert_ok!(result); + + // Verify that the swap was scheduled + assert_eq!( + ColdkeySwapDestinations::::get(regular_user), + vec![new_coldkey] ); }); } diff --git a/runtime/src/lib.rs b/runtime/src/lib.rs index 9be039e6b..f020c5183 100644 --- a/runtime/src/lib.rs +++ b/runtime/src/lib.rs @@ -315,6 +315,7 @@ impl Contains for SafeModeWhitelistedCalls { pallet_subtensor::Call::schedule_coldkey_swap { .. } | pallet_subtensor::Call::set_weights { .. } | pallet_subtensor::Call::set_root_weights { .. } + | pallet_subtensor::Call::serve_axon { .. } ) ) } From e30c8f4599573159297a48253eb3359d4a3337c9 Mon Sep 17 00:00:00 2001 From: Samuel Dare Date: Tue, 9 Jul 2024 18:52:02 +0400 Subject: [PATCH 46/51] feat: coldkey swap info --- node/src/rpc.rs | 1 + pallets/subtensor/rpc/src/lib.rs | 68 ++++++- pallets/subtensor/runtime-api/src/lib.rs | 6 + pallets/subtensor/src/lib.rs | 1 + .../src/schedule_coldkey_swap_info.rs | 172 ++++++++++++++++++ pallets/subtensor/src/swap.rs | 18 +- pallets/subtensor/tests/staking.rs | 104 +++++------ runtime/src/lib.rs | 17 ++ 8 files changed, 325 insertions(+), 62 deletions(-) create mode 100644 pallets/subtensor/src/schedule_coldkey_swap_info.rs diff --git a/node/src/rpc.rs b/node/src/rpc.rs index 54f82447f..c169b8530 100644 --- a/node/src/rpc.rs +++ b/node/src/rpc.rs @@ -60,6 +60,7 @@ where C::Api: subtensor_custom_rpc_runtime_api::NeuronInfoRuntimeApi, C::Api: subtensor_custom_rpc_runtime_api::SubnetInfoRuntimeApi, C::Api: subtensor_custom_rpc_runtime_api::SubnetRegistrationRuntimeApi, + C::Api: subtensor_custom_rpc_runtime_api::ColdkeySwapRuntimeApi, B: sc_client_api::Backend + Send + Sync + 'static, P: TransactionPool + 'static, { diff --git a/pallets/subtensor/rpc/src/lib.rs b/pallets/subtensor/rpc/src/lib.rs index 2f71e9c21..15157be2f 100644 --- a/pallets/subtensor/rpc/src/lib.rs +++ b/pallets/subtensor/rpc/src/lib.rs @@ -12,7 +12,7 @@ use std::sync::Arc; use sp_api::ProvideRuntimeApi; pub use subtensor_custom_rpc_runtime_api::{ - DelegateInfoRuntimeApi, NeuronInfoRuntimeApi, SubnetInfoRuntimeApi, + ColdkeySwapRuntimeApi, DelegateInfoRuntimeApi, NeuronInfoRuntimeApi, SubnetInfoRuntimeApi, SubnetRegistrationRuntimeApi, }; @@ -51,6 +51,24 @@ pub trait SubtensorCustomApi { #[method(name = "subnetInfo_getLockCost")] fn get_network_lock_cost(&self, at: Option) -> RpcResult; + #[method(name = "coldkeySwap_getScheduledColdkeySwap")] + fn get_scheduled_coldkey_swap( + &self, + coldkey_account_vec: Vec, + at: Option, + ) -> RpcResult>; + #[method(name = "coldkeySwap_getRemainingArbitrationPeriod")] + fn get_remaining_arbitration_period( + &self, + coldkey_account_vec: Vec, + at: Option, + ) -> RpcResult>; + #[method(name = "coldkeySwap_getColdkeySwapDestinations")] + fn get_coldkey_swap_destinations( + &self, + coldkey_account_vec: Vec, + at: Option, + ) -> RpcResult>; } pub struct SubtensorCustom { @@ -99,6 +117,7 @@ where C::Api: NeuronInfoRuntimeApi, C::Api: SubnetInfoRuntimeApi, C::Api: SubnetRegistrationRuntimeApi, + C::Api: ColdkeySwapRuntimeApi, { fn get_delegates(&self, at: Option<::Hash>) -> RpcResult> { let api = self.client.runtime_api(); @@ -223,4 +242,51 @@ where Error::RuntimeError(format!("Unable to get subnet lock cost: {:?}", e)).into() }) } + + fn get_scheduled_coldkey_swap( + &self, + coldkey_account_vec: Vec, + at: Option<::Hash>, + ) -> RpcResult> { + let api = self.client.runtime_api(); + let at = at.unwrap_or_else(|| self.client.info().best_hash); + + api.get_scheduled_coldkey_swap(at, coldkey_account_vec) + .map_err(|e| { + Error::RuntimeError(format!("Unable to get scheduled coldkey swap: {:?}", e)).into() + }) + } + + fn get_remaining_arbitration_period( + &self, + coldkey_account_vec: Vec, + at: Option<::Hash>, + ) -> RpcResult> { + let api = self.client.runtime_api(); + let at = at.unwrap_or_else(|| self.client.info().best_hash); + + api.get_remaining_arbitration_period(at, coldkey_account_vec) + .map_err(|e| { + Error::RuntimeError(format!( + "Unable to get remaining arbitration period: {:?}", + e + )) + .into() + }) + } + + fn get_coldkey_swap_destinations( + &self, + coldkey_account_vec: Vec, + at: Option<::Hash>, + ) -> RpcResult> { + let api = self.client.runtime_api(); + let at = at.unwrap_or_else(|| self.client.info().best_hash); + + api.get_coldkey_swap_destinations(at, coldkey_account_vec) + .map_err(|e| { + Error::RuntimeError(format!("Unable to get coldkey swap destinations: {:?}", e)) + .into() + }) + } } diff --git a/pallets/subtensor/runtime-api/src/lib.rs b/pallets/subtensor/runtime-api/src/lib.rs index 9095ad54a..a647d3619 100644 --- a/pallets/subtensor/runtime-api/src/lib.rs +++ b/pallets/subtensor/runtime-api/src/lib.rs @@ -32,4 +32,10 @@ sp_api::decl_runtime_apis! { pub trait SubnetRegistrationRuntimeApi { fn get_network_registration_cost() -> u64; } + + pub trait ColdkeySwapRuntimeApi { + fn get_scheduled_coldkey_swap( coldkey_account_vec: Vec ) -> Vec; + fn get_remaining_arbitration_period( coldkey_account_vec: Vec ) -> Vec; + fn get_coldkey_swap_destinations( coldkey_account_vec: Vec ) -> Vec; + } } diff --git a/pallets/subtensor/src/lib.rs b/pallets/subtensor/src/lib.rs index 787989f17..a1abff24c 100644 --- a/pallets/subtensor/src/lib.rs +++ b/pallets/subtensor/src/lib.rs @@ -51,6 +51,7 @@ mod weights; pub mod delegate_info; pub mod neuron_info; +pub mod schedule_coldkey_swap_info; pub mod stake_info; pub mod subnet_info; diff --git a/pallets/subtensor/src/schedule_coldkey_swap_info.rs b/pallets/subtensor/src/schedule_coldkey_swap_info.rs new file mode 100644 index 000000000..0408cc835 --- /dev/null +++ b/pallets/subtensor/src/schedule_coldkey_swap_info.rs @@ -0,0 +1,172 @@ +use super::*; +use codec::Compact; +use frame_support::pallet_prelude::{Decode, Encode}; +use sp_core::hexdisplay::AsBytesRef; + +#[derive(Decode, Encode, PartialEq, Eq, Clone, Debug)] +pub struct ScheduledColdkeySwapInfo { + old_coldkey: T::AccountId, + new_coldkey: T::AccountId, + arbitration_block: Compact, +} + +impl Pallet { + /// Retrieves the scheduled coldkey swap information for an existing account. + /// + /// # Arguments + /// + /// * `coldkey` - The account ID of the coldkey to check. + /// + /// # Returns + /// + /// * `Option>` - The scheduled coldkey swap information if it exists, otherwise `None`. + /// + /// # Notes + /// + /// This function checks if there are any destination coldkeys associated with the given coldkey. + /// If there are, it retrieves the arbitration block and constructs the `ScheduledColdkeySwapInfo` struct. + fn get_scheduled_coldkey_swap_by_existing_account( + coldkey: AccountIdOf, + ) -> Option> { + let destinations: Vec = ColdkeySwapDestinations::::get(&coldkey); + if destinations.is_empty() { + return None; + } + + let arbitration_block: u64 = ColdkeyArbitrationBlock::::get(&coldkey); + + Some(ScheduledColdkeySwapInfo { + old_coldkey: coldkey, + new_coldkey: destinations[0].clone(), + arbitration_block: arbitration_block.into(), + }) + } + + /// Retrieves the scheduled coldkey swap information for a given coldkey account vector. + /// + /// # Arguments + /// + /// * `coldkey_account_vec` - The vector of bytes representing the coldkey account. + /// + /// # Returns + /// + /// * `Option>` - The scheduled coldkey swap information if it exists, otherwise `None`. + /// + /// # Notes + /// + /// This function decodes the coldkey account vector into an account ID and then calls + /// `get_scheduled_coldkey_swap_by_existing_account` to retrieve the swap information. + pub fn get_scheduled_coldkey_swap( + coldkey_account_vec: Vec, + ) -> Option> { + if coldkey_account_vec.len() != 32 { + return None; + } + + let coldkey: AccountIdOf = + T::AccountId::decode(&mut coldkey_account_vec.as_bytes_ref()).ok()?; + Self::get_scheduled_coldkey_swap_by_existing_account(coldkey) + } + + /// Retrieves all scheduled coldkey swaps from storage. + /// + /// # Returns + /// + /// * `Vec>` - A vector containing all scheduled coldkey swap information. + /// + /// # Notes + /// + /// This function iterates over all coldkeys in `ColdkeySwapDestinations` and retrieves their swap information + /// using `get_scheduled_coldkey_swap_by_existing_account`. + pub fn get_all_scheduled_coldkey_swaps() -> Vec> { + let mut scheduled_swaps: Vec> = Vec::new(); + for coldkey in ColdkeySwapDestinations::::iter_keys() { + if let Some(swap_info) = Self::get_scheduled_coldkey_swap_by_existing_account(coldkey) { + scheduled_swaps.push(swap_info); + } + } + scheduled_swaps + } + + /// Retrieves the scheduled coldkey swaps for a given block. + /// + /// # Arguments + /// + /// * `block` - The block number to check for scheduled coldkey swaps. + /// + /// # Returns + /// + /// * `Vec>` - A vector containing the scheduled coldkey swap information for the given block. + /// + /// # Notes + /// + /// This function retrieves the coldkeys to swap at the given block and then retrieves their swap information + /// using `get_scheduled_coldkey_swap_by_existing_account`. + pub fn get_scheduled_coldkey_swaps_at_block(block: u64) -> Vec> { + let coldkeys_to_swap: Vec = ColdkeysToSwapAtBlock::::get(block); + let mut scheduled_swaps: Vec> = Vec::new(); + for coldkey in coldkeys_to_swap { + if let Some(swap_info) = Self::get_scheduled_coldkey_swap_by_existing_account(coldkey) { + scheduled_swaps.push(swap_info); + } + } + scheduled_swaps + } + + /// Retrieves the remaining arbitration period for a given coldkey account vector. + /// + /// # Arguments + /// + /// * `coldkey_account_vec` - The vector of bytes representing the coldkey account. + /// + /// # Returns + /// + /// * `Option` - The remaining arbitration period in blocks if it exists, otherwise `None`. + /// + /// # Notes + /// + /// This function decodes the coldkey account vector into an account ID and calculates the remaining arbitration period + /// by subtracting the current block number from the arbitration block number. + pub fn get_remaining_arbitration_period(coldkey_account_vec: Vec) -> Option { + if coldkey_account_vec.len() != 32 { + return None; + } + + let coldkey: AccountIdOf = + T::AccountId::decode(&mut coldkey_account_vec.as_bytes_ref()).ok()?; + let current_block: u64 = Self::get_current_block_as_u64(); + let arbitration_block: u64 = ColdkeyArbitrationBlock::::get(&coldkey); + + if arbitration_block > current_block { + Some(arbitration_block.saturating_sub(current_block)) + } else { + Some(0) + } + } + + /// Retrieves the destination coldkeys for a given coldkey account vector. + /// + /// # Arguments + /// + /// * `coldkey_account_vec` - The vector of bytes representing the coldkey account. + /// + /// # Returns + /// + /// * `Option>` - A vector containing the destination coldkeys if they exist, otherwise `None`. + /// + /// # Notes + /// + /// This function decodes the coldkey account vector into an account ID and retrieves the destination coldkeys + /// from `ColdkeySwapDestinations`. + pub fn get_coldkey_swap_destinations( + coldkey_account_vec: Vec, + ) -> Option> { + if coldkey_account_vec.len() != 32 { + return None; + } + + let coldkey: AccountIdOf = + T::AccountId::decode(&mut coldkey_account_vec.as_bytes_ref()).ok()?; + Some(ColdkeySwapDestinations::::get(&coldkey)) + } +} diff --git a/pallets/subtensor/src/swap.rs b/pallets/subtensor/src/swap.rs index 7a548f289..da4edc656 100644 --- a/pallets/subtensor/src/swap.rs +++ b/pallets/subtensor/src/swap.rs @@ -189,15 +189,15 @@ impl Pallet { /// /// This function calculates the remaining arbitration period by subtracting the current block number /// from the arbitration block number of the coldkey. - pub fn get_remaining_arbitration_period(coldkey: &T::AccountId) -> u64 { - let current_block: u64 = Self::get_current_block_as_u64(); - let arbitration_block: u64 = ColdkeyArbitrationBlock::::get(coldkey); - if arbitration_block > current_block { - arbitration_block.saturating_sub(current_block) - } else { - 0 - } - } + // pub fn get_remaining_arbitration_period(coldkey: &T::AccountId) -> u64 { + // let current_block: u64 = Self::get_current_block_as_u64(); + // let arbitration_block: u64 = ColdkeyArbitrationBlock::::get(coldkey); + // if arbitration_block > current_block { + // arbitration_block.saturating_sub(current_block) + // } else { + // 0 + // } + // } pub fn meets_min_allowed_coldkey_balance(coldkey: &T::AccountId) -> bool { let all_staked_keys: Vec = StakingHotkeys::::get(coldkey); diff --git a/pallets/subtensor/tests/staking.rs b/pallets/subtensor/tests/staking.rs index 873c81309..b275e5f0c 100644 --- a/pallets/subtensor/tests/staking.rs +++ b/pallets/subtensor/tests/staking.rs @@ -3788,58 +3788,58 @@ fn test_concurrent_arbitrated_coldkey_swaps() { }); } -#[test] -fn test_get_remaining_arbitration_period() { - new_test_ext(1).execute_with(|| { - let coldkey_account_id = U256::from(12345); // arbitrary coldkey - let new_coldkey_account_id = U256::from(54321); // arbitrary new coldkey - - let current_block = SubtensorModule::get_current_block_as_u64(); - let (work, nonce) = generate_valid_pow( - &coldkey_account_id, - current_block, - U256::from(BaseDifficulty::::get()), - ); - - SubtensorModule::add_balance_to_coldkey_account( - &coldkey_account_id, - MIN_BALANCE_TO_PERFORM_COLDKEY_SWAP, - ); - - // Schedule a coldkey swap to set the arbitration block - assert_ok!(SubtensorModule::do_schedule_coldkey_swap( - &coldkey_account_id.clone(), - &new_coldkey_account_id, - work.to_fixed_bytes().to_vec(), - current_block, - nonce - )); - - // Get the current block number and arbitration period - let current_block: u64 = SubtensorModule::get_current_block_as_u64(); - let arbitration_period: u64 = ArbitrationPeriod::::get(); - log::info!("arbitration_period: {:?}", arbitration_period); - let arbitration_block: u64 = current_block + arbitration_period; - log::info!("arbitration_block: {:?}", arbitration_block); - - // Check if the remaining arbitration period is correct - let remaining_period = - SubtensorModule::get_remaining_arbitration_period(&coldkey_account_id); - assert_eq!(remaining_period, arbitration_period); - - // Move the current block forward and check again - step_block(50); - let remaining_period = - SubtensorModule::get_remaining_arbitration_period(&coldkey_account_id); - assert_eq!(remaining_period, arbitration_period - 50); - - // Move the current block beyond the arbitration block and check again - step_block((arbitration_period as u16) - 50 + 1); - let remaining_period = - SubtensorModule::get_remaining_arbitration_period(&coldkey_account_id); - assert_eq!(remaining_period, 0); - }); -} +// #[test] +// fn test_get_remaining_arbitration_period() { +// new_test_ext(1).execute_with(|| { +// let coldkey_account_id = U256::from(12345); // arbitrary coldkey +// let new_coldkey_account_id = U256::from(54321); // arbitrary new coldkey + +// let current_block = SubtensorModule::get_current_block_as_u64(); +// let (work, nonce) = generate_valid_pow( +// &coldkey_account_id, +// current_block, +// U256::from(BaseDifficulty::::get()), +// ); + +// SubtensorModule::add_balance_to_coldkey_account( +// &coldkey_account_id, +// MIN_BALANCE_TO_PERFORM_COLDKEY_SWAP, +// ); + +// // Schedule a coldkey swap to set the arbitration block +// assert_ok!(SubtensorModule::do_schedule_coldkey_swap( +// &coldkey_account_id.clone(), +// &new_coldkey_account_id, +// work.to_fixed_bytes().to_vec(), +// current_block, +// nonce +// )); + +// // Get the current block number and arbitration period +// let current_block: u64 = SubtensorModule::get_current_block_as_u64(); +// let arbitration_period: u64 = ArbitrationPeriod::::get(); +// log::info!("arbitration_period: {:?}", arbitration_period); +// let arbitration_block: u64 = current_block + arbitration_period; +// log::info!("arbitration_block: {:?}", arbitration_block); + +// // Check if the remaining arbitration period is correct +// let remaining_period = +// SubtensorModule::get_remaining_arbitration_period(&coldkey_account_id); +// assert_eq!(remaining_period, arbitration_period); + +// // Move the current block forward and check again +// step_block(50); +// let remaining_period = +// SubtensorModule::get_remaining_arbitration_period(&coldkey_account_id); +// assert_eq!(remaining_period, arbitration_period - 50); + +// // Move the current block beyond the arbitration block and check again +// step_block((arbitration_period as u16) - 50 + 1); +// let remaining_period = +// SubtensorModule::get_remaining_arbitration_period(&coldkey_account_id); +// assert_eq!(remaining_period, 0); +// }); +// } #[test] fn test_transfer_coldkey_in_arbitration() { diff --git a/runtime/src/lib.rs b/runtime/src/lib.rs index f020c5183..f0d830c38 100644 --- a/runtime/src/lib.rs +++ b/runtime/src/lib.rs @@ -1663,6 +1663,23 @@ impl_runtime_apis! { SubtensorModule::get_network_lock_cost() } } + + impl subtensor_custom_rpc_runtime_api::ColdkeySwapRuntimeApi for Runtime { + fn get_scheduled_coldkey_swap( coldkey_account_vec: Vec ) -> Vec { + let result = SubtensorModule::get_scheduled_coldkey_swap( coldkey_account_vec ); + result.encode() + } + + fn get_remaining_arbitration_period( coldkey_account_vec: Vec ) -> Vec { + let result = SubtensorModule::get_remaining_arbitration_period( coldkey_account_vec ); + result.encode() + } + + fn get_coldkey_swap_destinations( coldkey_account_vec: Vec ) -> Vec { + let result = SubtensorModule::get_coldkey_swap_destinations( coldkey_account_vec ); + result.encode() + } + } } // #[cfg(test)] From 7aaeb090deddbeb98e8baf1740016afc021e240c Mon Sep 17 00:00:00 2001 From: Samuel Dare Date: Tue, 9 Jul 2024 21:52:40 +0400 Subject: [PATCH 47/51] feat: move back to on_initialise, lints --- pallets/subtensor/src/delegate_info.rs | 4 +- pallets/subtensor/src/lib.rs | 48 +++++++------ .../src/schedule_coldkey_swap_info.rs | 6 +- pallets/subtensor/src/staking.rs | 71 ------------------- pallets/subtensor/src/swap.rs | 22 +++--- pallets/subtensor/tests/staking.rs | 1 + runtime/src/lib.rs | 2 +- 7 files changed, 48 insertions(+), 106 deletions(-) diff --git a/pallets/subtensor/src/delegate_info.rs b/pallets/subtensor/src/delegate_info.rs index 7ae50355a..e1eab1210 100644 --- a/pallets/subtensor/src/delegate_info.rs +++ b/pallets/subtensor/src/delegate_info.rs @@ -142,7 +142,7 @@ impl Pallet { for (delegator, stake) in Stake::::iter_prefix(&hotkey) { if delegator != owner { - total_delegated += stake; + total_delegated = total_delegated.saturating_add(stake); } } } @@ -162,7 +162,7 @@ impl Pallet { // Iterate through all delegators for this hotkey for (delegator, stake) in Stake::::iter_prefix(hotkey) { if delegator != Self::get_coldkey_for_hotkey(hotkey) { - total_delegated += stake; + total_delegated = total_delegated.saturating_add(stake); } } diff --git a/pallets/subtensor/src/lib.rs b/pallets/subtensor/src/lib.rs index a1abff24c..6fdc7cc6e 100644 --- a/pallets/subtensor/src/lib.rs +++ b/pallets/subtensor/src/lib.rs @@ -1343,40 +1343,48 @@ pub mod pallet { #[pallet::hooks] impl Hooks> for Pallet { - fn on_idle(_n: BlockNumberFor, remaining_weight: Weight) -> Weight { - // Unstake and transfer pending coldkeys up to the remaining weight - match Self::swap_coldkeys_this_block(&remaining_weight) { + fn on_idle(_n: BlockNumberFor, _remaining_weight: Weight) -> Weight { + Weight::zero() + } + + fn on_initialize(_block_number: BlockNumberFor) -> Weight { + let mut total_weight = Weight::zero(); + + // Create a Weight::MAX value to pass to swap_coldkeys_this_block + let max_weight = Weight::MAX; + + // Perform coldkey swapping + let swap_weight = match Self::swap_coldkeys_this_block(&max_weight) { Ok(weight_used) => weight_used, Err(e) => { log::error!("Error while swapping coldkeys: {:?}", e); - Weight::default() + Weight::zero() } - } - } + }; + total_weight = total_weight.saturating_add(swap_weight); - // ---- Called on the initialization of this pallet. (the order of on_finalize calls is determined in the runtime) - // - // # Args: - // * 'n': (BlockNumberFor): - // - The number of the block we are initializing. - fn on_initialize(_block_number: BlockNumberFor) -> Weight { + // Perform block step let block_step_result = Self::block_step(); match block_step_result { Ok(_) => { - // --- If the block step was successful, return the weight. log::debug!("Successfully ran block step."); - Weight::from_parts(110_634_229_000_u64, 0) - .saturating_add(T::DbWeight::get().reads(8304_u64)) - .saturating_add(T::DbWeight::get().writes(110_u64)) + total_weight = total_weight.saturating_add( + Weight::from_parts(110_634_229_000_u64, 0) + .saturating_add(T::DbWeight::get().reads(8304_u64)) + .saturating_add(T::DbWeight::get().writes(110_u64)), + ); } Err(e) => { - // --- If the block step was unsuccessful, return the weight anyway. log::error!("Error while stepping block: {:?}", e); - Weight::from_parts(110_634_229_000_u64, 0) - .saturating_add(T::DbWeight::get().reads(8304_u64)) - .saturating_add(T::DbWeight::get().writes(110_u64)) + total_weight = total_weight.saturating_add( + Weight::from_parts(110_634_229_000_u64, 0) + .saturating_add(T::DbWeight::get().reads(8304_u64)) + .saturating_add(T::DbWeight::get().writes(110_u64)), + ); } } + + total_weight } fn on_runtime_upgrade() -> frame_support::weights::Weight { diff --git a/pallets/subtensor/src/schedule_coldkey_swap_info.rs b/pallets/subtensor/src/schedule_coldkey_swap_info.rs index 0408cc835..e23ad23b6 100644 --- a/pallets/subtensor/src/schedule_coldkey_swap_info.rs +++ b/pallets/subtensor/src/schedule_coldkey_swap_info.rs @@ -1,6 +1,7 @@ use super::*; use codec::Compact; use frame_support::pallet_prelude::{Decode, Encode}; + use sp_core::hexdisplay::AsBytesRef; #[derive(Decode, Encode, PartialEq, Eq, Clone, Debug)] @@ -37,7 +38,10 @@ impl Pallet { Some(ScheduledColdkeySwapInfo { old_coldkey: coldkey, - new_coldkey: destinations[0].clone(), + new_coldkey: destinations.first().cloned().unwrap_or_else(|| { + T::AccountId::decode(&mut sp_runtime::traits::TrailingZeroInput::zeroes()) + .expect("Infinite length input; no invalid inputs for type; qed") + }), arbitration_block: arbitration_block.into(), }) } diff --git a/pallets/subtensor/src/staking.rs b/pallets/subtensor/src/staking.rs index 3499bdda5..199234a30 100644 --- a/pallets/subtensor/src/staking.rs +++ b/pallets/subtensor/src/staking.rs @@ -868,75 +868,4 @@ impl Pallet { Self::add_balance_to_coldkey_account(&delegate_coldkey_i, stake_i); } } - - // pub fn x( coldkey_a: &T::AccountId, coldkey_b: &T::AccountId ) -> Weight { - // let mut weight = frame_support::weights::Weight::from_parts(0, 0); - - // // Get the hotkeys associated with coldkey_a. - // let coldkey_a_hotkeys: Vec = StakingHotkeys::::get( &coldkey_a ); - // weight = weight.saturating_add(T::DbWeight::get().reads_writes(1, 0)); - - // // Iterate over all the hotkeys associated with this coldkey - // for hotkey_i in coldkey_a_hotkeys.iter() { - - // // Get the current stake from coldkey_a to hotkey_i. - // let all_current_stake_i: u64 = Self::get_stake_for_coldkey_and_hotkey( &coldkey_a, &hotkey_i ); - // weight = weight.saturating_add(T::DbWeight::get().reads_writes(1, 0)); - - // // We remove the balance from the hotkey acount equal to all of it. - // Self::decrease_stake_on_coldkey_hotkey_account( &coldkey_a, &hotkey_i, all_current_stake_i ); - // weight = weight.saturating_add(T::DbWeight::get().reads_writes(0, 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_a, all_current_stake_i ); - // weight = weight.saturating_add(T::DbWeight::get().reads_writes(2, 2)); - // } - // Unstake all delegate stake make by this coldkey to non-owned hotkeys - // let staking_hotkeys = StakingHotkeys::::get(¤t_coldkey); - - // // iterate over all staking hotkeys. - // for hotkey in staking_hotkeys { - // // Get the current stake - // let current_stake: u64 = - // Self::get_stake_for_coldkey_and_hotkey(¤t_coldkey, &hotkey); - - // // Unstake all balance if there's any stake - // if current_stake > 0 { - // Self::do_remove_stake( - // RawOrigin::Signed(current_coldkey.clone()).into(), - // hotkey.clone(), - // current_stake, - // )?; - // } - // } - - // let total_balance = Self::get_coldkey_balance(¤t_coldkey); - // log::info!("Total Bank Balance: {:?}", total_balance); - - // // Get the total balance here. - // let total_balance = Self::get_coldkey_balance( &coldkey_a ); - // weight = weight.saturating_add(T::DbWeight::get().reads_writes(1, 0)); - - // if !total_balance.is_zero() { - // // Attempt to transfer the entire total balance to coldkey_b. - // let _ = T::Currency::transfer( - // coldkey_a, - // coldkey_b, - // total_balance, - // Preservation::Expendable, - // ); - // weight = weight.saturating_add(T::DbWeight::get().reads_writes(1, 1)); - // } - - // // Emit event - // Self::deposit_event( - // Event::AllBalanceUnstakedAndTransferredToNewColdkey { - // current_coldkey: coldkey_a.clone(), - // new_coldkey: coldkey_b.clone(), - // total_balance - // } - // ); - - // weight - // } } diff --git a/pallets/subtensor/src/swap.rs b/pallets/subtensor/src/swap.rs index da4edc656..24d67aeb4 100644 --- a/pallets/subtensor/src/swap.rs +++ b/pallets/subtensor/src/swap.rs @@ -203,9 +203,11 @@ impl Pallet { let all_staked_keys: Vec = StakingHotkeys::::get(coldkey); let mut total_staking_balance: u64 = 0; for hotkey in all_staked_keys { - total_staking_balance += Self::get_stake_for_coldkey_and_hotkey(coldkey, &hotkey); + total_staking_balance = total_staking_balance + .saturating_add(Self::get_stake_for_coldkey_and_hotkey(coldkey, &hotkey)); } - total_staking_balance += Self::get_coldkey_balance(coldkey); + total_staking_balance = + total_staking_balance.saturating_add(Self::get_coldkey_balance(coldkey)); total_staking_balance >= MIN_BALANCE_TO_PERFORM_COLDKEY_SWAP } @@ -414,15 +416,13 @@ impl Pallet { // Swap the coldkey. let total_balance: u64 = Self::get_coldkey_balance(old_coldkey); if total_balance > 0 { - // Attempt to transfer the entire total balance to coldkeyB. - if let Err(e) = T::Currency::transfer( + // Attempt to transfer the entire total balance to new_coldkey. + T::Currency::transfer( old_coldkey, new_coldkey, total_balance, Preservation::Expendable, - ) { - return Err(e.into()); - } + )?; } Ok(weight) @@ -832,7 +832,7 @@ impl Pallet { Owner::::insert(hotkey, new_coldkey); // Update the transaction weight - *weight += T::DbWeight::get().reads_writes(2, 2); + weight.saturating_accrue(T::DbWeight::get().reads_writes(2, 2)); } } @@ -858,18 +858,18 @@ impl Pallet { ); // Update the transaction weight - *weight += T::DbWeight::get().reads_writes(2, 2); + weight.saturating_accrue(T::DbWeight::get().reads_writes(2, 2)); } // Update the list of owned hotkeys for both old and new coldkeys OwnedHotkeys::::remove(old_coldkey); OwnedHotkeys::::insert(new_coldkey, old_owned_hotkeys); - *weight += T::DbWeight::get().reads_writes(1, 2); + weight.saturating_accrue(T::DbWeight::get().reads_writes(1, 2)); // Update the staking hotkeys for both old and new coldkeys let staking_hotkeys: Vec = StakingHotkeys::::take(old_coldkey); StakingHotkeys::::insert(new_coldkey, staking_hotkeys); - *weight += T::DbWeight::get().reads_writes(1, 1); + weight.saturating_accrue(T::DbWeight::get().reads_writes(1, 1)); // Log the total stake of old and new coldkeys after the swap log::info!( diff --git a/pallets/subtensor/tests/staking.rs b/pallets/subtensor/tests/staking.rs index b275e5f0c..fdbc2521d 100644 --- a/pallets/subtensor/tests/staking.rs +++ b/pallets/subtensor/tests/staking.rs @@ -1,4 +1,5 @@ #![allow(clippy::unwrap_used)] +#![allow(clippy::arithmetic_side_effects)] use frame_support::pallet_prelude::{ InvalidTransaction, TransactionValidity, TransactionValidityError, diff --git a/runtime/src/lib.rs b/runtime/src/lib.rs index f0d830c38..a58bff272 100644 --- a/runtime/src/lib.rs +++ b/runtime/src/lib.rs @@ -139,7 +139,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: 189, + spec_version: 187, impl_version: 1, apis: RUNTIME_API_VERSIONS, transaction_version: 1, From 3817b766ef2247e564fb5550749fae41bd57aecf Mon Sep 17 00:00:00 2001 From: Samuel Dare Date: Tue, 9 Jul 2024 23:55:11 +0400 Subject: [PATCH 48/51] chore: bump arb delay back to 3 days --- pallets/subtensor/src/lib.rs | 2 +- pallets/subtensor/tests/staking.rs | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/pallets/subtensor/src/lib.rs b/pallets/subtensor/src/lib.rs index 6fdc7cc6e..a74b3956d 100644 --- a/pallets/subtensor/src/lib.rs +++ b/pallets/subtensor/src/lib.rs @@ -411,7 +411,7 @@ pub mod pallet { /// This value represents the default arbitration period in blocks. /// The period is set to 18 hours, assuming a block time of 12 seconds. pub fn DefaultArbitrationPeriod() -> u64 { - 5400 // 18 hours * 60 minutes/hour * 5 blocks/minute + 7200 * 3 // 3 days } #[pallet::storage] // ---- StorageItem Global Used Work. pub type ArbitrationPeriod = diff --git a/pallets/subtensor/tests/staking.rs b/pallets/subtensor/tests/staking.rs index fdbc2521d..5db439e5b 100644 --- a/pallets/subtensor/tests/staking.rs +++ b/pallets/subtensor/tests/staking.rs @@ -3192,7 +3192,7 @@ fn test_arbitrated_coldkey_swap_success() { ); // Check that drain block is set correctly - let drain_block: u64 = 5400 + 1; + let drain_block: u64 = 7200 * 3 + 1; log::info!( "ColdkeysToSwapAtBlock before scheduling: {:?}", @@ -3399,7 +3399,7 @@ fn test_arbitrated_coldkey_swap_with_no_stake() { )); // Make 5400 blocks pass, simulating on_idle for each block - let drain_block: u64 = 5400 + 1; + let drain_block: u64 = 7200 * 3 + 1; for _ in 0..drain_block { next_block(); SubtensorModule::on_idle(System::block_number(), Weight::MAX); @@ -3463,7 +3463,7 @@ fn test_arbitrated_coldkey_swap_with_multiple_stakes() { )); // Make 5400 blocks pass, simulating on_idle for each block - let drain_block: u64 = 5400 + 1; + let drain_block: u64 = 7200 * 3 + 1; for _ in 0..drain_block { next_block(); SubtensorModule::on_idle(System::block_number(), Weight::MAX); @@ -3548,7 +3548,7 @@ fn test_arbitrated_coldkey_swap_multiple_arbitrations() { ); // Simulate the passage of blocks and on_idle calls - for i in 0..(5400 + 1) { + for i in 0..(7200 * 3 + 1) { next_block(); SubtensorModule::on_idle(System::block_number(), Weight::MAX); @@ -3771,7 +3771,7 @@ fn test_concurrent_arbitrated_coldkey_swaps() { nonce2 )); // Make 5400 blocks pass - let drain_block: u64 = 5400 + 1; + let drain_block: u64 = 7200 * 3 + 1; run_to_block(drain_block); // Run arbitration From 380aa6508307b2cb8b949c032320a33db26bdcfc Mon Sep 17 00:00:00 2001 From: Samuel Dare Date: Tue, 9 Jul 2024 23:59:26 +0400 Subject: [PATCH 49/51] chore: reduce min tao req to 0.1 --- pallets/subtensor/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pallets/subtensor/src/lib.rs b/pallets/subtensor/src/lib.rs index a74b3956d..dd3f1a4c1 100644 --- a/pallets/subtensor/src/lib.rs +++ b/pallets/subtensor/src/lib.rs @@ -86,7 +86,7 @@ pub mod pallet { const STORAGE_VERSION: StorageVersion = StorageVersion::new(6); /// Minimum balance required to perform a coldkey swap - pub const MIN_BALANCE_TO_PERFORM_COLDKEY_SWAP: u64 = 1_000_000_000; // 1 TAO in RAO + pub const MIN_BALANCE_TO_PERFORM_COLDKEY_SWAP: u64 = 1_000_000_00; // 0.1 TAO in RAO #[pallet::pallet] #[pallet::without_storage_info] From fec59cb1f3e90290bb1528a99fe234cb7b178c03 Mon Sep 17 00:00:00 2001 From: Samuel Dare Date: Wed, 10 Jul 2024 00:19:28 +0400 Subject: [PATCH 50/51] feat: whitelist set_commitment --- runtime/src/lib.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/runtime/src/lib.rs b/runtime/src/lib.rs index a58bff272..dca8cfe98 100644 --- a/runtime/src/lib.rs +++ b/runtime/src/lib.rs @@ -317,6 +317,7 @@ impl Contains for SafeModeWhitelistedCalls { | pallet_subtensor::Call::set_root_weights { .. } | pallet_subtensor::Call::serve_axon { .. } ) + | RuntimeCall::Commitments(pallet_commitments::Call::set_commitment { .. }) ) } } From 9de2afba8e1cff8000f4fb518eaffafc4d952ab0 Mon Sep 17 00:00:00 2001 From: Samuel Dare Date: Wed, 10 Jul 2024 00:44:11 +0400 Subject: [PATCH 51/51] spec verion , lints --- pallets/subtensor/src/lib.rs | 2 +- runtime/src/lib.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pallets/subtensor/src/lib.rs b/pallets/subtensor/src/lib.rs index dd3f1a4c1..0560ca72c 100644 --- a/pallets/subtensor/src/lib.rs +++ b/pallets/subtensor/src/lib.rs @@ -86,7 +86,7 @@ pub mod pallet { const STORAGE_VERSION: StorageVersion = StorageVersion::new(6); /// Minimum balance required to perform a coldkey swap - pub const MIN_BALANCE_TO_PERFORM_COLDKEY_SWAP: u64 = 1_000_000_00; // 0.1 TAO in RAO + pub const MIN_BALANCE_TO_PERFORM_COLDKEY_SWAP: u64 = 100_000_000; // 0.1 TAO in RAO #[pallet::pallet] #[pallet::without_storage_info] diff --git a/runtime/src/lib.rs b/runtime/src/lib.rs index dca8cfe98..3e5af1b43 100644 --- a/runtime/src/lib.rs +++ b/runtime/src/lib.rs @@ -139,7 +139,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: 187, + spec_version: 194, impl_version: 1, apis: RUNTIME_API_VERSIONS, transaction_version: 1,