@@ -81,6 +81,9 @@ fn migrate_pending_emissions_including_null_stake<T: Config>(
8181 weight
8282}
8383
84+ // This executes the migration to fix the pending emissions
85+ // This also migrates the stake entry of (old_hotkey, 0x000) to the Migration Account for
86+ // both the old hotkeys.
8487pub fn do_migrate_fix_pending_emission < T : Config > ( ) -> Weight {
8588 // Initialize the weight with one read operation.
8689 let mut weight = T :: DbWeight :: get ( ) . reads ( 1 ) ;
@@ -139,39 +142,310 @@ pub fn do_migrate_fix_pending_emission<T: Config>() -> Weight {
139142
140143 weight
141144}
142- // Public migrate function to be called by Lib.rs on upgrade.
143- pub fn migrate_fix_pending_emission < T : Config > ( ) -> Weight {
144- let migration_name = b"fix_pending_emission" . to_vec ( ) ;
145145
146- // Initialize the weight with one read operation.
147- let mut weight = T :: DbWeight :: get ( ) . reads ( 1 ) ;
146+ /// Collection of storage item formats from the previous storage version.
147+ ///
148+ /// Required so we can read values in the v0 storage format during the migration.
149+ #[ cfg( feature = "try-runtime" ) ]
150+ mod v0 {
151+ use frame_support:: storage_alias;
152+ use subtensor_macros:: freeze_struct;
148153
149- // Check if the migration has already run
150- if HasMigrationRun :: < T > :: get ( & migration_name) {
151- log:: info!(
152- "Migration '{:?}' has already run. Skipping." ,
153- migration_name
154- ) ;
155- return Weight :: zero ( ) ;
154+ #[ freeze_struct( "7adbed79987ae83d" ) ]
155+ #[ derive( codec:: Encode , codec:: Decode , Clone , PartialEq , Debug ) ]
156+ pub struct OldStorage {
157+ pub total_issuance_before : u64 ,
158+ pub expected_taostats_new_hk_pending_emission : u64 ,
159+ pub expected_datura_new_hk_pending_emission : u64 ,
160+ pub old_null_stake_taostats : u64 ,
161+ pub old_null_stake_datura : u64 ,
156162 }
157163
158- log :: info! (
159- "Running migration '{}'" ,
160- String :: from_utf8_lossy ( & migration_name )
161- ) ;
164+ /// V0 type for [`crate::Value`].
165+ # [ storage_alias ]
166+ pub type Value < T : crate :: Config > = StorageValue < crate :: Pallet < T > , OldStorage > ;
167+ }
162168
163- // Run the migration
164- weight. saturating_accrue ( do_migrate_fix_pending_emission :: < T > ( ) ) ;
169+ impl < T : Config > Pallet < T > {
170+ #[ cfg( feature = "try-runtime" ) ]
171+ fn check_null_stake_invariants (
172+ old_storage : v0:: OldStorage ,
173+ ) -> Result < ( ) , sp_runtime:: TryRuntimeError > {
174+ let null_account = & DefaultAccount :: < T > :: get ( ) ;
165175
166- // Mark the migration as completed
167- HasMigrationRun :: < T > :: insert ( & migration_name , true ) ;
168- weight . saturating_accrue ( T :: DbWeight :: get ( ) . writes ( 1 ) ) ;
176+ let taostats_old_hotkey = "5Hddm3iBFD2GLT5ik7LZnT3XJUnRnN8PoeCFgGQgawUVKNm8" ;
177+ let taostats_new_hotkey = "5GKH9FPPnWSUoeeTJp19wVtd84XqFW4pyK2ijV2GsFbhTrP1" ;
178+ let migration_coldkey = "5GeRjQYsobRWFnrbBmGe5ugme3rfnDVF69N45YtdBpUFsJG8" ;
169179
170- log:: info!(
171- "Migration '{:?}' completed. Marked in storage." ,
172- String :: from_utf8_lossy( & migration_name)
173- ) ;
180+ let taostats_old_hk_account = & get_account_id_from_ss58 :: < T > ( taostats_old_hotkey) ;
181+ let taostats_new_hk_account = & get_account_id_from_ss58 :: < T > ( taostats_new_hotkey) ;
182+ let migration_ck_account = & get_account_id_from_ss58 :: < T > ( migration_coldkey) ;
174183
175- // Return the migration weight.
176- weight
184+ let old = old_storage;
185+ let null_stake_total = old
186+ . old_null_stake_taostats
187+ . saturating_add ( old. old_null_stake_datura ) ;
188+
189+ match (
190+ taostats_old_hk_account,
191+ taostats_new_hk_account,
192+ migration_ck_account,
193+ ) {
194+ ( Ok ( taostats_old_hk_acct) , Ok ( taostats_new_hk_acct) , Ok ( migration_ck_acct) ) => {
195+ // Check the pending emission is added to new the TaoStats hotkey
196+ assert_eq ! (
197+ PendingdHotkeyEmission :: <T >:: get( taostats_new_hk_acct) ,
198+ old. expected_taostats_new_hk_pending_emission
199+ ) ;
200+
201+ assert_eq ! ( PendingdHotkeyEmission :: <T >:: get( taostats_old_hk_acct) , 0 ) ;
202+
203+ assert_eq ! ( Stake :: <T >:: get( taostats_old_hk_acct, null_account) , 0 ) ;
204+
205+ assert ! ( StakingHotkeys :: <T >:: get( migration_ck_acct) . contains( taostats_old_hk_acct) ) ;
206+
207+ assert_eq ! (
208+ Self :: get_stake_for_coldkey_and_hotkey( null_account, taostats_old_hk_acct) ,
209+ 0
210+ ) ;
211+
212+ let new_null_stake_taostats =
213+ Self :: get_stake_for_coldkey_and_hotkey ( migration_ck_acct, taostats_old_hk_acct) ;
214+
215+ assert_eq ! ( new_null_stake_taostats, old. old_null_stake_taostats) ;
216+ }
217+ _ => {
218+ log:: warn!( "Failed to get account id from ss58 for taostats hotkeys" ) ;
219+ return Err ( "Failed to get account id from ss58 for taostats hotkeys" . into ( ) ) ;
220+ }
221+ }
222+
223+ let datura_old_hotkey = "5FKstHjZkh4v3qAMSBa1oJcHCLjxYZ8SNTSz1opTv4hR7gVB" ;
224+ let datura_new_hotkey = "5GP7c3fFazW9GXK8Up3qgu2DJBk8inu4aK9TZy3RuoSWVCMi" ;
225+
226+ let datura_old_hk_account = & get_account_id_from_ss58 :: < T > ( datura_old_hotkey) ;
227+ let datura_new_hk_account = & get_account_id_from_ss58 :: < T > ( datura_new_hotkey) ;
228+
229+ match (
230+ datura_old_hk_account,
231+ datura_new_hk_account,
232+ migration_ck_account,
233+ ) {
234+ ( Ok ( datura_old_hk_acct) , Ok ( datura_new_hk_acct) , Ok ( migration_ck_acct) ) => {
235+ // Check the pending emission is added to new Datura hotkey
236+ assert_eq ! (
237+ crate :: PendingdHotkeyEmission :: <T >:: get( datura_new_hk_acct) ,
238+ old. expected_datura_new_hk_pending_emission
239+ ) ;
240+
241+ // Check the pending emission is removed from old ones
242+ assert_eq ! ( PendingdHotkeyEmission :: <T >:: get( datura_old_hk_acct) , 0 ) ;
243+
244+ // Check the stake entry is removed
245+ assert_eq ! ( Stake :: <T >:: get( datura_old_hk_acct, null_account) , 0 ) ;
246+
247+ assert ! ( StakingHotkeys :: <T >:: get( migration_ck_acct) . contains( datura_old_hk_acct) ) ;
248+
249+ assert_eq ! (
250+ Self :: get_stake_for_coldkey_and_hotkey( null_account, datura_old_hk_acct) ,
251+ 0
252+ ) ;
253+
254+ let new_null_stake_datura =
255+ Self :: get_stake_for_coldkey_and_hotkey ( migration_ck_acct, datura_old_hk_acct) ;
256+
257+ assert_eq ! ( new_null_stake_datura, old. old_null_stake_datura) ;
258+ }
259+ _ => {
260+ log:: warn!( "Failed to get account id from ss58 for datura hotkeys" ) ;
261+ return Err ( "Failed to get account id from ss58 for datura hotkeys" . into ( ) ) ;
262+ }
263+ }
264+
265+ match migration_ck_account {
266+ Ok ( migration_ck_acct) => {
267+ // Check the migration key has stake with both *old* hotkeys
268+ assert_eq ! (
269+ TotalColdkeyStake :: <T >:: get( migration_ck_acct) ,
270+ null_stake_total
271+ ) ;
272+ }
273+ _ => {
274+ log:: warn!( "Failed to get account id from ss58 for migration coldkey" ) ;
275+ return Err ( "Failed to get account id from ss58 for migration coldkey" . into ( ) ) ;
276+ }
277+ }
278+
279+ // Check the total issuance is the SAME following migration (no TAO issued)
280+ let expected_total_issuance = old. total_issuance_before ;
281+ assert_eq ! ( Self :: get_total_issuance( ) , expected_total_issuance) ;
282+
283+ // Check total stake is the SAME following the migration (no new TAO staked)
284+ assert_eq ! ( TotalStake :: <T >:: get( ) , expected_total_issuance) ;
285+ // Check the total stake maps are updated following the migration (removal of old null_account stake entries)
286+ assert_eq ! ( TotalColdkeyStake :: <T >:: get( null_account) , 0 ) ;
287+
288+ // Check staking hotkeys is updated
289+ assert_eq ! ( StakingHotkeys :: <T >:: get( null_account) , vec![ ] ) ;
290+
291+ Ok ( ( ) )
292+ }
293+ }
294+
295+ pub mod migration {
296+ use frame_support:: pallet_prelude:: Weight ;
297+ use frame_support:: traits:: OnRuntimeUpgrade ;
298+ use sp_core:: Get ;
299+
300+ use super :: * ;
301+
302+ pub struct Migration < T : Config > ( PhantomData < T > ) ;
303+
304+ #[ cfg( feature = "try-runtime" ) ]
305+ fn get_old_storage_values < T : Config > ( ) -> Result < v0:: OldStorage , sp_runtime:: TryRuntimeError > {
306+ let null_account = & DefaultAccount :: < T > :: get ( ) ;
307+
308+ let taostats_old_hotkey = "5Hddm3iBFD2GLT5ik7LZnT3XJUnRnN8PoeCFgGQgawUVKNm8" ;
309+ let taostats_new_hotkey = "5GKH9FPPnWSUoeeTJp19wVtd84XqFW4pyK2ijV2GsFbhTrP1" ;
310+
311+ let taostats_old_hk_account = & get_account_id_from_ss58 :: < T > ( taostats_old_hotkey) ;
312+ let taostats_new_hk_account = & get_account_id_from_ss58 :: < T > ( taostats_new_hotkey) ;
313+
314+ let total_issuance_before = crate :: Pallet :: < T > :: get_total_issuance ( ) ;
315+ let mut expected_taostats_new_hk_pending_emission: u64 = 0 ;
316+ let mut expected_datura_new_hk_pending_emission: u64 = 0 ;
317+ let old_null_stake_taostats: u64 = match ( taostats_old_hk_account, taostats_new_hk_account)
318+ {
319+ ( Ok ( taostats_old_hk_acct) , Ok ( taostats_new_hk_acct) ) => {
320+ expected_taostats_new_hk_pending_emission =
321+ expected_taostats_new_hk_pending_emission
322+ . saturating_add ( PendingdHotkeyEmission :: < T > :: get ( taostats_old_hk_acct) )
323+ . saturating_add ( PendingdHotkeyEmission :: < T > :: get ( taostats_new_hk_acct) ) ;
324+
325+ Ok :: < u64 , sp_runtime:: TryRuntimeError > (
326+ crate :: Pallet :: < T > :: get_stake_for_coldkey_and_hotkey (
327+ null_account,
328+ taostats_old_hk_acct,
329+ ) ,
330+ )
331+ }
332+ _ => {
333+ log:: warn!( "Failed to get account id from ss58 for taostats hotkeys" ) ;
334+ Err ( "Failed to get account id from ss58 for taostats hotkeys" . into ( ) )
335+ }
336+ } ?;
337+
338+ let datura_old_hotkey = "5FKstHjZkh4v3qAMSBa1oJcHCLjxYZ8SNTSz1opTv4hR7gVB" ;
339+ let datura_new_hotkey = "5GP7c3fFazW9GXK8Up3qgu2DJBk8inu4aK9TZy3RuoSWVCMi" ;
340+
341+ let datura_old_hk_account = & get_account_id_from_ss58 :: < T > ( datura_old_hotkey) ;
342+ let datura_new_hk_account = & get_account_id_from_ss58 :: < T > ( datura_new_hotkey) ;
343+
344+ let old_null_stake_datura: u64 = match ( datura_old_hk_account, datura_new_hk_account) {
345+ ( Ok ( datura_old_hk_acct) , Ok ( datura_new_hk_acct) ) => {
346+ expected_datura_new_hk_pending_emission = expected_datura_new_hk_pending_emission
347+ . saturating_add ( PendingdHotkeyEmission :: < T > :: get ( datura_old_hk_acct) )
348+ . saturating_add ( PendingdHotkeyEmission :: < T > :: get ( datura_new_hk_acct) ) ;
349+
350+ Ok :: < u64 , sp_runtime:: TryRuntimeError > (
351+ crate :: Pallet :: < T > :: get_stake_for_coldkey_and_hotkey (
352+ null_account,
353+ datura_old_hk_acct,
354+ ) ,
355+ )
356+ }
357+ _ => {
358+ log:: warn!( "Failed to get account id from ss58 for datura hotkeys" ) ;
359+ Err ( "Failed to get account id from ss58 for datura hotkeys" . into ( ) )
360+ }
361+ } ?;
362+
363+ let result = v0:: OldStorage {
364+ total_issuance_before,
365+ expected_taostats_new_hk_pending_emission,
366+ expected_datura_new_hk_pending_emission,
367+ old_null_stake_taostats,
368+ old_null_stake_datura,
369+ } ;
370+
371+ Ok ( result)
372+ }
373+
374+ impl < T : Config > OnRuntimeUpgrade for Migration < T > {
375+ /// Runs the migration to fix the pending emissions.
376+ #[ cfg( feature = "try-runtime" ) ]
377+ fn pre_upgrade ( ) -> Result < Vec < u8 > , sp_runtime:: TryRuntimeError > {
378+ use codec:: Encode ;
379+
380+ // Get the old storage values
381+ let old_storage = get_old_storage_values :: < T > ( ) ?;
382+
383+ // Return it as an encoded `Vec<u8>`
384+ Ok ( old_storage. encode ( ) )
385+ }
386+
387+ // Runs the migrate function for the fix_pending_emission migration
388+ fn on_runtime_upgrade ( ) -> Weight {
389+ let migration_name = b"fix_pending_emission" . to_vec ( ) ;
390+
391+ // Initialize the weight with one read operation.
392+ let mut weight = T :: DbWeight :: get ( ) . reads ( 1 ) ;
393+
394+ // Check if the migration has already run
395+ if HasMigrationRun :: < T > :: get ( & migration_name) {
396+ log:: info!(
397+ "Migration '{:?}' has already run. Skipping." ,
398+ migration_name
399+ ) ;
400+ return Weight :: zero ( ) ;
401+ }
402+
403+ log:: info!(
404+ "Running migration '{}'" ,
405+ String :: from_utf8_lossy( & migration_name)
406+ ) ;
407+
408+ // Run the migration
409+ weight. saturating_accrue (
410+ migrations:: migrate_fix_pending_emission:: do_migrate_fix_pending_emission :: < T > ( ) ,
411+ ) ;
412+
413+ // Mark the migration as completed
414+ HasMigrationRun :: < T > :: insert ( & migration_name, true ) ;
415+ weight. saturating_accrue ( T :: DbWeight :: get ( ) . writes ( 1 ) ) ;
416+
417+ log:: info!(
418+ "Migration '{:?}' completed. Marked in storage." ,
419+ String :: from_utf8_lossy( & migration_name)
420+ ) ;
421+
422+ // Return the migration weight.
423+ weight
424+ }
425+
426+ /// Performs post-upgrade checks to ensure the migration was successful.
427+ ///
428+ /// This function is only compiled when the "try-runtime" feature is enabled.
429+ #[ cfg( feature = "try-runtime" ) ]
430+ fn post_upgrade ( state : Vec < u8 > ) -> Result < ( ) , sp_runtime:: TryRuntimeError > {
431+ use codec:: Decode ;
432+
433+ let maybe_old_value =
434+ Option :: < v0:: OldStorage > :: decode ( & mut & state[ ..] ) . map_err ( |_| {
435+ sp_runtime:: TryRuntimeError :: Other ( "Failed to decode old value from storage" )
436+ } ) ?;
437+
438+ match maybe_old_value {
439+ Some ( old_value) => {
440+ // Verify that all null stake invariants are satisfied after the migration
441+ crate :: Pallet :: < T > :: check_null_stake_invariants ( old_value) ?;
442+ }
443+ None => {
444+ log:: warn!( "Failed to decode old value from storage" ) ;
445+ return Err ( "Failed to decode old value from storage" . into ( ) ) ;
446+ }
447+ } ;
448+ Ok ( ( ) )
449+ }
450+ }
177451}
0 commit comments