diff --git a/pallets/subtensor/src/swap/swap_hotkey.rs b/pallets/subtensor/src/swap/swap_hotkey.rs index efa6c53f8..b16c180c4 100644 --- a/pallets/subtensor/src/swap/swap_hotkey.rs +++ b/pallets/subtensor/src/swap/swap_hotkey.rs @@ -337,7 +337,20 @@ impl Pallet { // Remove the old hotkey's child entries ChildKeys::::remove(old_hotkey, netuid); // Insert the same child entries for the new hotkey - ChildKeys::::insert(new_hotkey, netuid, my_children); + ChildKeys::::insert(new_hotkey, netuid, my_children.clone()); + for (_, child_key_i) in my_children { + // For each child, update their parent list + let mut child_parents: Vec<(u64, T::AccountId)> = + ParentKeys::::get(child_key_i.clone(), netuid); + for parent in child_parents.iter_mut() { + // If the parent is the old hotkey, replace it with the new hotkey + if parent.1 == *old_hotkey { + parent.1 = new_hotkey.clone(); + } + } + // Update the child's parent list + ParentKeys::::insert(child_key_i, netuid, child_parents); + } } // 13. Swap ParentKeys. diff --git a/pallets/subtensor/tests/swap_hotkey.rs b/pallets/subtensor/tests/swap_hotkey.rs index ad4d0414c..59d114cf1 100644 --- a/pallets/subtensor/tests/swap_hotkey.rs +++ b/pallets/subtensor/tests/swap_hotkey.rs @@ -9,6 +9,7 @@ use mock::*; use pallet_subtensor::*; use sp_core::H256; use sp_core::U256; +use sp_runtime::SaturatedConversion; // SKIP_WASM_BUILD=1 RUST_LOG=debug cargo test --test swap_hotkey -- test_swap_owner --exact --nocapture #[test] @@ -1148,3 +1149,188 @@ fn test_swap_complex_parent_child_structure() { ); }); } + +// SKIP_WASM_BUILD=1 RUST_LOG=debug cargo test --test swap_hotkey -- test_hotkey_swap_stake_delta --exact --nocapture +#[test] +fn test_hotkey_swap_stake_delta() { + new_test_ext(1).execute_with(|| { + let old_hotkey = U256::from(3); + let new_hotkey = U256::from(4); + let coldkey = U256::from(7); + + let coldkeys = [U256::from(1), U256::from(2), U256::from(5)]; + + let mut weight = Weight::zero(); + + // Set up initial state + // Add stake delta for each coldkey and the old_hotkey + for &coldkey in coldkeys.iter() { + StakeDeltaSinceLastEmissionDrain::::insert( + old_hotkey, + coldkey, + (123 + coldkey.saturated_into::()), + ); + + StakingHotkeys::::insert(coldkey, vec![old_hotkey]); + } + + // Add stake delta for one coldkey and the new_hotkey + StakeDeltaSinceLastEmissionDrain::::insert(new_hotkey, coldkeys[0], 456); + // Add corresponding StakingHotkeys + StakingHotkeys::::insert(coldkeys[0], vec![old_hotkey, new_hotkey]); + + // Perform the swap + SubtensorModule::perform_hotkey_swap(&old_hotkey, &new_hotkey, &coldkey, &mut weight); + + // Ensure the stake delta is correctly transferred for each coldkey + // -- coldkey[0] maintains its stake delta from the new_hotkey and the old_hotkey + assert_eq!( + StakeDeltaSinceLastEmissionDrain::::get(new_hotkey, coldkeys[0]), + 123 + coldkeys[0].saturated_into::() + 456 + ); + // -- coldkey[1..] maintains its stake delta from the old_hotkey + for &coldkey in coldkeys[1..].iter() { + assert_eq!( + StakeDeltaSinceLastEmissionDrain::::get(new_hotkey, coldkey), + 123 + coldkey.saturated_into::() + ); + assert!(!StakeDeltaSinceLastEmissionDrain::::contains_key( + old_hotkey, coldkey + )); + } + }); +} + +// SKIP_WASM_BUILD=1 RUST_LOG=debug cargo test --test swap_hotkey -- test_swap_hotkey_with_pending_emissions --exact --nocapture +#[test] +fn test_swap_hotkey_with_pending_emissions() { + new_test_ext(1).execute_with(|| { + let old_hotkey = U256::from(1); + let new_hotkey = U256::from(2); + let coldkey = U256::from(3); + let netuid = 0u16; + let mut weight = Weight::zero(); + + let pending_emission = 123_456_789u64; + + // Set up initial state + add_network(netuid, 0, 1); + + // Set up pending emissions + PendingdHotkeyEmission::::insert(old_hotkey, pending_emission); + // Verify the pending emissions are set + assert_eq!( + PendingdHotkeyEmission::::get(old_hotkey), + pending_emission + ); + // Verify the new hotkey does not have any pending emissions + assert!(!PendingdHotkeyEmission::::contains_key(new_hotkey)); + + // Perform the swap + SubtensorModule::perform_hotkey_swap(&old_hotkey, &new_hotkey, &coldkey, &mut weight); + + // Verify the pending emissions are transferred + assert_eq!( + PendingdHotkeyEmission::::get(new_hotkey), + pending_emission + ); + assert!(!PendingdHotkeyEmission::::contains_key(old_hotkey)); + }); +} + +#[test] +fn test_swap_parent_hotkey_childkey_maps() { + new_test_ext(1).execute_with(|| { + let netuid: u16 = 1; + let parent_old = U256::from(1); + let coldkey = U256::from(2); + let child = U256::from(3); + let parent_new = U256::from(4); + add_network(netuid, 0, 0); + SubtensorModule::create_account_if_non_existent(&coldkey, &parent_old); + + // Set child and verify state maps + assert_ok!(SubtensorModule::do_set_children( + RuntimeOrigin::signed(coldkey), + parent_old, + netuid, + vec![(u64::MAX, child)] + )); + assert_eq!( + ParentKeys::::get(child, netuid), + vec![(u64::MAX, parent_old)] + ); + assert_eq!( + ChildKeys::::get(parent_old, netuid), + vec![(u64::MAX, child)] + ); + + // Swap + let mut weight = Weight::zero(); + assert_ok!(SubtensorModule::perform_hotkey_swap( + &parent_old, + &parent_new, + &coldkey, + &mut weight + )); + + // Verify parent and child keys updates + assert_eq!( + ParentKeys::::get(child, netuid), + vec![(u64::MAX, parent_new)] + ); + assert_eq!( + ChildKeys::::get(parent_new, netuid), + vec![(u64::MAX, child)] + ); + }) +} + +#[test] +fn test_swap_child_hotkey_childkey_maps() { + new_test_ext(1).execute_with(|| { + let netuid: u16 = 1; + let parent = U256::from(1); + let coldkey = U256::from(2); + let child_old = U256::from(3); + let child_new = U256::from(4); + add_network(netuid, 0, 0); + SubtensorModule::create_account_if_non_existent(&coldkey, &child_old); + SubtensorModule::create_account_if_non_existent(&coldkey, &parent); + + // Set child and verify state maps + assert_ok!(SubtensorModule::do_set_children( + RuntimeOrigin::signed(coldkey), + parent, + netuid, + vec![(u64::MAX, child_old)] + )); + assert_eq!( + ParentKeys::::get(child_old, netuid), + vec![(u64::MAX, parent)] + ); + assert_eq!( + ChildKeys::::get(parent, netuid), + vec![(u64::MAX, child_old)] + ); + + // Swap + let mut weight = Weight::zero(); + assert_ok!(SubtensorModule::perform_hotkey_swap( + &child_old, + &child_new, + &coldkey, + &mut weight + )); + + // Verify parent and child keys updates + assert_eq!( + ParentKeys::::get(child_new, netuid), + vec![(u64::MAX, parent)] + ); + assert_eq!( + ChildKeys::::get(parent, netuid), + vec![(u64::MAX, child_new)] + ); + }) +} diff --git a/support/procedural-fork/src/pallet/parse/storage.rs b/support/procedural-fork/src/pallet/parse/storage.rs index 811832427..64a5e685b 100644 --- a/support/procedural-fork/src/pallet/parse/storage.rs +++ b/support/procedural-fork/src/pallet/parse/storage.rs @@ -718,11 +718,11 @@ fn process_generics( "CountedStorageNMap" => StorageKind::CountedNMap, found => { let msg = format!( - "Invalid pallet::storage, expected ident: `StorageValue` or \ + "Invalid pallet::storage, expected ident: `StorageValue` or \ `StorageMap` or `CountedStorageMap` or `StorageDoubleMap` or `StorageNMap` or `CountedStorageNMap` \ in order to expand metadata, found `{}`.", - found, - ); + found, + ); return Err(syn::Error::new(segment.ident.span(), msg)); } }; diff --git a/support/procedural-fork/src/runtime/parse/mod.rs b/support/procedural-fork/src/runtime/parse/mod.rs index a6a49e814..494ab2c53 100644 --- a/support/procedural-fork/src/runtime/parse/mod.rs +++ b/support/procedural-fork/src/runtime/parse/mod.rs @@ -255,19 +255,19 @@ impl Def { }; let def = Def { - input, - runtime_struct: runtime_struct.ok_or_else(|| { - syn::Error::new(item_span, + input, + runtime_struct: runtime_struct.ok_or_else(|| { + syn::Error::new(item_span, "Missing Runtime. Please add a struct inside the module and annotate it with `#[runtime::runtime]`" ) - })?, - pallets, - runtime_types: runtime_types.ok_or_else(|| { - syn::Error::new(item_span, + })?, + pallets, + runtime_types: runtime_types.ok_or_else(|| { + syn::Error::new(item_span, "Missing Runtime Types. Please annotate the runtime struct with `#[runtime::derive]`" ) - })?, - }; + })?, + }; Ok(def) }