diff --git a/.gitignore b/.gitignore index f394de80c..5921b6b93 100644 --- a/.gitignore +++ b/.gitignore @@ -6,6 +6,8 @@ **/*.lock +*.ipynb + # Generated by code coverage *.profraw *.profdata diff --git a/docs/delegate-info.json b/docs/delegate-info.json index bda41b48e..46355da3d 100644 --- a/docs/delegate-info.json +++ b/docs/delegate-info.json @@ -390,5 +390,12 @@ "url": "https://cortex.foundation/", "description": "Cortex Foundation is committed to advancing the integration of decentralized AI. Our validator is designed for transparency, reliability, and community engagement.", "signature": "7a6274ff6b0f7ddca97e37ef4a9b90781012ff3cf7baa3159f6feaafc43c557975aad324ea608d6b8abeb21f8f3ca2595e54b81a7564574d0242b803d969618a" + }, + { + "address":"5F27Eqz2PhyMtGMEce898x31DokNqRVxkm5AhDDe6rDGNvoY", + "name": "Love", + "url": "https://love.cosimo.fund", + "description": "Love validator exists to accelerate open source AI and be good stewards of the Bittensorr network", + "signature": "c221a3de3be031c149a7be912b3b75e0355605f041dc975153302b23b4d93e45e9cc7453532491e92076ccd333a4c1f95f4a2229aae8f4fcfb88e5dec3f14c87" } ] \ No newline at end of file diff --git a/pallets/subtensor/src/benchmarks.rs b/pallets/subtensor/src/benchmarks.rs index 03e087a92..e30cf9027 100644 --- a/pallets/subtensor/src/benchmarks.rs +++ b/pallets/subtensor/src/benchmarks.rs @@ -429,4 +429,61 @@ reveal_weights { }: reveal_weights(RawOrigin::Signed(hotkey.clone()), netuid, uids, weight_values, salt, version_key) + swap_coldkey { + // Set up initial state + let old_coldkey: T::AccountId = account("old_coldkey", 0, 0); + let new_coldkey: T::AccountId = account("new_coldkey", 0, 0); + let hotkey1: T::AccountId = account("hotkey1", 0, 0); + let netuid = 1u16; + let stake_amount1 = 1000u64; + let stake_amount2 = 2000u64; + let swap_cost = Subtensor::::get_key_swap_cost(); + let free_balance_old = 12345u64 + swap_cost; + let tempo: u16 = 1; + + // Setup initial state + Subtensor::::init_new_network(netuid, tempo); + Subtensor::::set_network_registration_allowed(netuid, true); + Subtensor::::set_network_pow_registration_allowed(netuid, true); + + 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, + &hotkey1, + ); + + let _ = Subtensor::::register( + ::RuntimeOrigin::from(RawOrigin::Signed(old_coldkey.clone())), + netuid, + block_number, + nonce, + work.clone(), + hotkey1.clone(), + old_coldkey.clone(), + ); + + // Add balance to old coldkey + Subtensor::::add_balance_to_coldkey_account( + &old_coldkey, + stake_amount1 + stake_amount2 + free_balance_old, + ); + + // Insert an Identity + let name: Vec = b"The fourth Coolest Identity".to_vec(); + let identity: ChainIdentity = ChainIdentity { + name: name.clone(), + url: vec![], + image: vec![], + discord: vec![], + description: vec![], + additional: vec![], + }; + + Identities::::insert(&old_coldkey, identity); + + // Benchmark setup complete, now execute the extrinsic +}: swap_coldkey(RawOrigin::Signed(old_coldkey.clone()), new_coldkey.clone()) + } diff --git a/pallets/subtensor/src/macros/dispatches.rs b/pallets/subtensor/src/macros/dispatches.rs index 00865f8db..8057e9dd4 100644 --- a/pallets/subtensor/src/macros/dispatches.rs +++ b/pallets/subtensor/src/macros/dispatches.rs @@ -668,9 +668,10 @@ mod dispatches { /// /// Weight is calculated based on the number of database reads and writes. #[pallet::call_index(71)] - #[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(127_713_000, 0) + .saturating_add(Weight::from_parts(0, 11645)) + .saturating_add(T::DbWeight::get().reads(18)) + .saturating_add(T::DbWeight::get().writes(12)), DispatchClass::Operational, Pays::No))] pub fn swap_coldkey( origin: OriginFor, new_coldkey: T::AccountId, diff --git a/pallets/subtensor/src/swap/swap_coldkey.rs b/pallets/subtensor/src/swap/swap_coldkey.rs index bd8a11fa1..b3fadfa70 100644 --- a/pallets/subtensor/src/swap/swap_coldkey.rs +++ b/pallets/subtensor/src/swap/swap_coldkey.rs @@ -51,7 +51,12 @@ impl Pallet { Error::::ColdKeyAlreadyAssociated ); - // 5. Calculate the swap cost and ensure sufficient balance + // 5. Swap the identity if the old coldkey has one + if let Some(identity) = Identities::::take(&old_coldkey) { + Identities::::insert(new_coldkey, identity); + } + + // 6. Calculate the swap cost and ensure sufficient balance let swap_cost = Self::get_key_swap_cost(); log::debug!("Coldkey swap cost: {:?}", swap_cost); ensure!( @@ -59,28 +64,28 @@ impl Pallet { Error::::NotEnoughBalanceToPaySwapColdKey ); - // 6. Remove and burn the swap cost from the old coldkey's account + // 7. Remove and burn the swap cost from the old coldkey's account let actual_burn_amount = Self::remove_balance_from_coldkey_account(&old_coldkey, swap_cost)?; Self::burn_tokens(actual_burn_amount); - // 7. Update the weight for the balance operations + // 8. Update the weight for the balance operations weight.saturating_accrue(T::DbWeight::get().reads_writes(1, 1)); - // 8. Perform the actual coldkey swap + // 9. Perform the actual coldkey swap let _ = Self::perform_swap_coldkey(&old_coldkey, new_coldkey, &mut weight); - // 9. Update the last transaction block for the new coldkey + // 10. Update the last transaction block for the new coldkey Self::set_last_tx_block(new_coldkey, Self::get_current_block_as_u64()); weight.saturating_accrue(T::DbWeight::get().writes(1)); - // 10. Emit the ColdkeySwapped event + // 11. Emit the ColdkeySwapped event Self::deposit_event(Event::ColdkeySwapped { old_coldkey: old_coldkey.clone(), new_coldkey: new_coldkey.clone(), }); - // 11. Return the result with the updated weight + // 12. Return the result with the updated weight Ok(Some(weight).into()) } diff --git a/pallets/subtensor/tests/swap_coldkey.rs b/pallets/subtensor/tests/swap_coldkey.rs index 9203e2b3f..a4fddfe90 100644 --- a/pallets/subtensor/tests/swap_coldkey.rs +++ b/pallets/subtensor/tests/swap_coldkey.rs @@ -523,6 +523,22 @@ fn test_do_swap_coldkey_success() { stake_amount2 )); + // Insert an Identity + let name: Vec = b"The fourth Coolest Identity".to_vec(); + let identity: ChainIdentity = ChainIdentity { + name: name.clone(), + url: vec![], + image: vec![], + discord: vec![], + description: vec![], + additional: vec![], + }; + + Identities::::insert(old_coldkey, identity.clone()); + + assert!(Identities::::get(old_coldkey).is_some()); + assert!(Identities::::get(new_coldkey).is_none()); + // Log state after adding stake log::info!( "Total stake after adding: {}", @@ -594,6 +610,14 @@ fn test_do_swap_coldkey_success() { "Total stake changed unexpectedly" ); + // Verify identities were swapped + assert!(Identities::::get(old_coldkey).is_none()); + assert!(Identities::::get(new_coldkey).is_some()); + assert_eq!( + Identities::::get(new_coldkey).expect("Expected an Identity"), + identity + ); + // Verify event emission System::assert_last_event( Event::ColdkeySwapped { @@ -1286,3 +1310,136 @@ fn test_coldkey_delegations() { assert_eq!(Stake::::get(delegate, coldkey), 0); }); } + +#[test] +fn test_coldkey_swap_delegate_identity_updated() { + new_test_ext(1).execute_with(|| { + let old_coldkey = U256::from(1); + let new_coldkey = U256::from(2); + + let netuid = 1; + let burn_cost = 10; + let tempo = 1; + + SubtensorModule::set_burn(netuid, burn_cost); + add_network(netuid, tempo, 0); + + SubtensorModule::add_balance_to_coldkey_account(&old_coldkey, 100_000_000_000); + + assert_ok!(SubtensorModule::burned_register( + <::RuntimeOrigin>::signed(old_coldkey), + netuid, + old_coldkey + )); + + let name: Vec = b"The Third Coolest Identity".to_vec(); + let identity: ChainIdentity = ChainIdentity { + name: name.clone(), + url: vec![], + image: vec![], + discord: vec![], + description: vec![], + additional: vec![], + }; + + Identities::::insert(old_coldkey, identity.clone()); + + assert!(Identities::::get(old_coldkey).is_some()); + assert!(Identities::::get(new_coldkey).is_none()); + + assert_ok!(SubtensorModule::do_swap_coldkey( + <::RuntimeOrigin>::signed(old_coldkey), + &new_coldkey + )); + + assert!(Identities::::get(old_coldkey).is_none()); + assert!(Identities::::get(new_coldkey).is_some()); + assert_eq!( + Identities::::get(new_coldkey).expect("Expected an Identity"), + identity + ); + }); +} + +#[test] +fn test_coldkey_swap_no_identity_no_changes() { + new_test_ext(1).execute_with(|| { + let old_coldkey = U256::from(1); + let new_coldkey = U256::from(2); + + let netuid = 1; + let burn_cost = 10; + let tempo = 1; + + SubtensorModule::set_burn(netuid, burn_cost); + add_network(netuid, tempo, 0); + + SubtensorModule::add_balance_to_coldkey_account(&old_coldkey, 100_000_000_000); + + assert_ok!(SubtensorModule::burned_register( + <::RuntimeOrigin>::signed(old_coldkey), + netuid, + old_coldkey + )); + + // Ensure the old coldkey does not have an identity before the swap + assert!(Identities::::get(old_coldkey).is_none()); + + // Perform the coldkey swap + assert_ok!(SubtensorModule::do_swap_coldkey( + <::RuntimeOrigin>::signed(old_coldkey), + &new_coldkey, + )); + + // Ensure no identities have been changed + assert!(Identities::::get(old_coldkey).is_none()); + assert!(Identities::::get(new_coldkey).is_none()); + }); +} + +#[test] +fn test_coldkey_swap_no_identity_no_changes_newcoldkey_exists() { + new_test_ext(1).execute_with(|| { + let old_coldkey_2 = U256::from(3); + let new_coldkey_2 = U256::from(4); + + let netuid = 1; + let burn_cost = 10; + let tempo = 1; + + SubtensorModule::set_burn(netuid, burn_cost); + add_network(netuid, tempo, 0); + SubtensorModule::add_balance_to_coldkey_account(&old_coldkey_2, 100_000_000_000); + + assert_ok!(SubtensorModule::burned_register( + <::RuntimeOrigin>::signed(old_coldkey_2), + netuid, + old_coldkey_2 + )); + + let name: Vec = b"The Coolest Identity".to_vec(); + let identity: ChainIdentity = ChainIdentity { + name: name.clone(), + url: vec![], + image: vec![], + discord: vec![], + description: vec![], + additional: vec![], + }; + + Identities::::insert(new_coldkey_2, identity.clone()); + // Ensure the new coldkey does have an identity before the swap + assert!(Identities::::get(new_coldkey_2).is_some()); + assert!(Identities::::get(old_coldkey_2).is_none()); + + // Perform the coldkey swap + assert_ok!(SubtensorModule::do_swap_coldkey( + <::RuntimeOrigin>::signed(old_coldkey_2), + &new_coldkey_2, + )); + + // Ensure no identities have been changed + assert!(Identities::::get(old_coldkey_2).is_none()); + assert!(Identities::::get(new_coldkey_2).is_some()); + }); +}