Skip to content

Commit 67aad02

Browse files
author
Cameron Fairchild
committed
add try runtime check for new migration
1 parent 625fc9c commit 67aad02

File tree

4 files changed

+306
-36
lines changed

4 files changed

+306
-36
lines changed

pallets/subtensor/src/macros/hooks.rs

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -70,9 +70,7 @@ mod hooks {
7070
// Storage version v8 -> v9
7171
.saturating_add(migrations::migrate_fix_total_coldkey_stake::migrate_fix_total_coldkey_stake::<T>())
7272
// Migrate Delegate Ids on chain
73-
.saturating_add(migrations::migrate_chain_identity::migrate_set_hotkey_identities::<T>())
74-
// Fix pending emissions
75-
.saturating_add(migrations::migrate_fix_pending_emission::migrate_fix_pending_emission::<T>());
73+
.saturating_add(migrations::migrate_chain_identity::migrate_set_hotkey_identities::<T>());
7674
weight
7775
}
7876

pallets/subtensor/src/migrations/migrate_fix_pending_emission.rs

Lines changed: 301 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -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.
8487
pub 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
}

pallets/subtensor/tests/migration.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -436,11 +436,11 @@ fn run_migration_and_check(migration_name: &'static str) -> frame_support::weigh
436436
fn run_pending_emissions_migration_and_check(
437437
migration_name: &'static str,
438438
) -> frame_support::weights::Weight {
439+
use frame_support::traits::OnRuntimeUpgrade;
440+
439441
// Execute the migration and store its weight
440442
let weight: frame_support::weights::Weight =
441-
pallet_subtensor::migrations::migrate_fix_pending_emission::migrate_fix_pending_emission::<
442-
Test,
443-
>();
443+
pallet_subtensor::migrations::migrate_fix_pending_emission::migration::Migration::<Test>::on_runtime_upgrade();
444444

445445
// Check if the migration has been marked as completed
446446
assert!(HasMigrationRun::<Test>::get(

runtime/src/lib.rs

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1076,9 +1076,7 @@ pub type SignedExtra = (
10761076
);
10771077

10781078
type Migrations =
1079-
(pallet_subtensor::migrations::migrate_fix_pending_emission::migrate_fix_pending_emission::Migration<
1080-
Runtime,
1081-
>);
1079+
(pallet_subtensor::migrations::migrate_fix_pending_emission::migration::Migration<Runtime>,);
10821080

10831081
// Unchecked extrinsic type as expected by this runtime.
10841082
pub type UncheckedExtrinsic =

0 commit comments

Comments
 (0)