@@ -2,6 +2,9 @@ use crate::*;
22use frame_support:: log;
33use pallet_balances:: ExtraFlags ;
44
5+ #[ cfg( feature = "try-runtime" ) ]
6+ use sp_std:: collections:: btree_map:: BTreeMap ;
7+
58mod prev {
69 use super :: * ;
710 use frame_support:: { pallet_prelude:: ValueQuery , storage_alias, Blake2_128Concat } ;
@@ -37,15 +40,27 @@ mod prev {
3740 > ;
3841}
3942
43+ #[ cfg( feature = "try-runtime" ) ]
44+ #[ derive( Encode , Decode ) ]
45+ enum PreUpgradeInfo {
46+ MigrationAlreadyOccured ,
47+ MigrationShouldRun (
48+ BTreeMap < AccountId , frame_system:: AccountInfo < u32 , pallet_balances:: AccountData < Balance > > > ,
49+ ) ,
50+ }
51+
4052const TARGET : & ' static str = "runtime::account_data_migration" ;
4153pub struct Migration ;
4254impl OnRuntimeUpgrade for Migration {
4355 /// Save pre-upgrade account ids to check are decodable post-upgrade.
4456 #[ cfg( feature = "try-runtime" ) ]
4557 fn pre_upgrade ( ) -> Result < Vec < u8 > , sp_runtime:: TryRuntimeError > {
46- use sp_std:: collections:: btree_map:: BTreeMap ;
47- log:: info!( target: TARGET , "pre-upgrade" ) ;
58+ // Skip migration if it already took place.
59+ if migration_already_occured ( ) {
60+ return Ok ( PreUpgradeInfo :: MigrationAlreadyOccured . encode ( ) ) ;
61+ }
4862
63+ log:: info!( target: TARGET , "pre-upgrade" ) ;
4964 // Save the expected post-migration account state.
5065 let mut expected_account: BTreeMap <
5166 AccountId ,
@@ -77,11 +92,20 @@ impl OnRuntimeUpgrade for Migration {
7792 expected_account. insert ( acc_id, expected_acc) ;
7893 }
7994
80- Ok ( expected_account. encode ( ) )
95+ Ok ( PreUpgradeInfo :: MigrationShouldRun ( expected_account) . encode ( ) )
8196 }
8297
8398 /// Migrates Account storage to the new format, and calls `ensure_upgraded` for them.
8499 fn on_runtime_upgrade ( ) -> Weight {
100+ // Skip migration if it already took place.
101+ if migration_already_occured ( ) {
102+ log:: warn!(
103+ target: TARGET ,
104+ "Migration already completed and can be removed." ,
105+ ) ;
106+ return <Runtime as frame_system:: Config >:: DbWeight :: get ( ) . reads_writes ( 0u64 , 0u64 ) ;
107+ }
108+
85109 // Pull the storage in the previous format into memory
86110 let accounts = prev:: Account :: < Runtime > :: iter ( ) . collect :: < Vec < _ > > ( ) ;
87111 log:: info!( target: TARGET , "Migrating {} accounts..." , accounts. len( ) ) ;
@@ -119,42 +143,63 @@ impl OnRuntimeUpgrade for Migration {
119143 #[ cfg( feature = "try-runtime" ) ]
120144 fn post_upgrade ( state : Vec < u8 > ) -> Result < ( ) , sp_runtime:: TryRuntimeError > {
121145 use frame_support:: ensure;
122- use sp_std:: collections:: btree_map:: BTreeMap ;
123146
124147 log:: info!( target: TARGET , "Running post-upgrade..." ) ;
125148
126- let expected_accounts: BTreeMap <
127- AccountId ,
128- frame_system:: AccountInfo < u32 , pallet_balances:: AccountData < Balance > > ,
129- > = Decode :: decode ( & mut & state[ ..] ) . expect ( "decoding state failed" ) ;
130-
131- // Ensure the actual post-migration state matches the expected
132- for ( acc_id, acc) in frame_system:: pallet:: Account :: < Runtime > :: iter ( ) {
133- let expected = expected_accounts. get ( & acc_id) . expect ( "account not found" ) ;
134-
135- // New system logic nukes the account if no providers or sufficients.
136- if acc. providers > 0 || acc. sufficients > 0 {
137- ensure ! ( acc. nonce == expected. nonce, "nonce mismatch" ) ;
138- ensure ! ( acc. consumers == expected. consumers, "consumers mismatch" ) ;
139- ensure ! ( acc. providers == expected. providers, "providers mismatch" ) ;
140- ensure ! (
141- acc. sufficients == expected. sufficients,
142- "sufficients mismatch"
143- ) ;
144- ensure ! ( acc. data. free == expected. data. free, "data.free mismatch" ) ;
145- ensure ! (
146- acc. data. reserved == expected. data. reserved,
147- "data.reserved mismatch"
148- ) ;
149- ensure ! (
150- acc. data. frozen == expected. data. frozen,
151- "data.frozen mismatch"
152- ) ;
153- ensure ! ( acc. data. flags == expected. data. flags, "data.flags mismatch" ) ;
149+ let pre_upgrade_data: PreUpgradeInfo =
150+ Decode :: decode ( & mut & state[ ..] ) . expect ( "decoding state failed" ) ;
151+
152+ match pre_upgrade_data {
153+ PreUpgradeInfo :: MigrationAlreadyOccured => Ok ( ( ) ) ,
154+ PreUpgradeInfo :: MigrationShouldRun ( expected_accounts) => {
155+ // Ensure the actual post-migration state matches the expected
156+ for ( acc_id, acc) in frame_system:: pallet:: Account :: < Runtime > :: iter ( ) {
157+ let expected = expected_accounts. get ( & acc_id) . expect ( "account not found" ) ;
158+
159+ // New system logic nukes the account if no providers or sufficients.
160+ if acc. providers > 0 || acc. sufficients > 0 {
161+ ensure ! ( acc. nonce == expected. nonce, "nonce mismatch" ) ;
162+ ensure ! ( acc. consumers == expected. consumers, "consumers mismatch" ) ;
163+ ensure ! ( acc. providers == expected. providers, "providers mismatch" ) ;
164+ ensure ! (
165+ acc. sufficients == expected. sufficients,
166+ "sufficients mismatch"
167+ ) ;
168+ ensure ! ( acc. data. free == expected. data. free, "data.free mismatch" ) ;
169+ ensure ! (
170+ acc. data. reserved == expected. data. reserved,
171+ "data.reserved mismatch"
172+ ) ;
173+ ensure ! (
174+ acc. data. frozen == expected. data. frozen,
175+ "data.frozen mismatch"
176+ ) ;
177+ ensure ! ( acc. data. flags == expected. data. flags, "data.flags mismatch" ) ;
178+ }
179+ }
180+
181+ log:: info!( target: TARGET , "post-upgrade success ✅" ) ;
182+ Ok ( ( ) )
154183 }
155184 }
185+ }
186+ }
156187
157- log:: info!( target: TARGET , "post-upgrade success ✅" ) ;
158- Ok ( ( ) )
188+ /// Check if the migration already took place. The migration is all-or-nothing, so if one
189+ /// account is migrated we know that the rest also must be migrated.
190+ ///
191+ /// We can use the nonce to check if it's been migrated, because it being zero meant that
192+ /// the decode succeeded and this account has been migrated already.
193+ ///
194+ /// Performance note: While this may appear poor for performance due to touching all keys,
195+ /// these keys will need to be touched anyway, so it's fine to just touch them loading them into
196+ /// the storage overlay here.
197+ fn migration_already_occured ( ) -> bool {
198+ for ( _, acc) in frame_system:: pallet:: Account :: < Runtime > :: iter ( ) {
199+ if acc. nonce > 0 {
200+ return true ;
201+ } ;
159202 }
203+
204+ false
160205}
0 commit comments