@@ -81,6 +81,9 @@ fn migrate_pending_emissions_including_null_stake<T: Config>(
81
81
weight
82
82
}
83
83
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.
84
87
pub fn do_migrate_fix_pending_emission < T : Config > ( ) -> Weight {
85
88
// Initialize the weight with one read operation.
86
89
let mut weight = T :: DbWeight :: get ( ) . reads ( 1 ) ;
@@ -139,39 +142,310 @@ pub fn do_migrate_fix_pending_emission<T: Config>() -> Weight {
139
142
140
143
weight
141
144
}
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 ( ) ;
145
145
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;
148
153
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 ,
156
162
}
157
163
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
+ }
162
168
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 ( ) ;
165
175
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" ;
169
179
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) ;
174
183
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
+ }
177
451
}
0 commit comments