pallet_revive/migrations/
v3.rs1use super::PALLET_MIGRATIONS_ID;
25use crate::{AddressMapper, Config, HoldReason, LOG_TARGET, weights::WeightInfo};
26use frame_support::{
27 migrations::{MigrationId, SteppedMigration, SteppedMigrationError},
28 pallet_prelude::PhantomData,
29 traits::{fungible::MutateHold, tokens::Precision},
30 weights::WeightMeter,
31};
32
33#[cfg(feature = "try-runtime")]
34extern crate alloc;
35
36#[cfg(feature = "try-runtime")]
37use alloc::vec::Vec;
38
39pub struct Migration<T: Config>(PhantomData<T>);
41
42impl<T: Config> SteppedMigration for Migration<T> {
43 type Cursor = T::AccountId;
44 type Identifier = MigrationId<17>;
45
46 fn id() -> Self::Identifier {
47 MigrationId { pallet_id: *PALLET_MIGRATIONS_ID, version_from: 2, version_to: 3 }
48 }
49
50 fn step(
51 mut cursor: Option<Self::Cursor>,
52 meter: &mut WeightMeter,
53 ) -> Result<Option<Self::Cursor>, SteppedMigrationError> {
54 let required = <T as Config>::WeightInfo::v3_migration_step();
55 if meter.remaining().any_lt(required) {
56 return Err(SteppedMigrationError::InsufficientWeight { required });
57 }
58
59 loop {
60 if meter.try_consume(required).is_err() {
61 break;
62 }
63
64 let mut iter = if let Some(ref last_key) = cursor {
65 frame_system::Account::<T>::iter_from(frame_system::Account::<T>::hashed_key_for(
66 last_key,
67 ))
68 } else {
69 frame_system::Account::<T>::iter()
70 };
71
72 if let Some((account_id, _)) = iter.next() {
73 if T::AddressMapper::is_eth_derived(&account_id) {
74 } else {
76 let _ = T::AddressMapper::map_no_deposit_unchecked(&account_id).inspect_err(
77 |err| {
78 log::debug!(
79 target: LOG_TARGET,
80 "Failed to map account {account_id:?}: {err:?}",
81 );
82 },
83 );
84
85 let _ = T::Currency::release_all(
86 &HoldReason::AddressMapping.into(),
87 &account_id,
88 Precision::BestEffort,
89 )
90 .inspect_err(|err| {
91 log::debug!(
92 target: LOG_TARGET,
93 "Failed to release mapping deposit for {account_id:?}: {err:?}",
94 );
95 });
96 }
97 cursor = Some(account_id);
98 } else {
99 cursor = None;
100 break;
101 }
102 }
103 Ok(cursor)
104 }
105
106 #[cfg(feature = "try-runtime")]
107 fn pre_upgrade() -> Result<Vec<u8>, sp_runtime::TryRuntimeError> {
108 use sp_core::Get;
109 assert!(T::AutoMap::get(), "v3 migration requires AutoMap to be enabled");
110
111 use codec::Encode;
112 let unmapped: u32 = frame_system::Account::<T>::iter_keys()
113 .filter(|id| !T::AddressMapper::is_mapped(id))
114 .count() as u32;
115 log::info!(target: LOG_TARGET, "v3: {unmapped} accounts to map");
116 Ok(unmapped.encode())
117 }
118
119 #[cfg(feature = "try-runtime")]
120 fn post_upgrade(prev: Vec<u8>) -> Result<(), sp_runtime::TryRuntimeError> {
121 use codec::Decode;
122 use frame_support::traits::fungible::InspectHold;
123 use sp_runtime::traits::Zero;
124
125 let prev_unmapped =
126 u32::decode(&mut &prev[..]).expect("Failed to decode pre_upgrade state");
127 let still_unmapped: u32 = frame_system::Account::<T>::iter_keys()
128 .filter(|id| !T::AddressMapper::is_mapped(id))
129 .count() as u32;
130 assert_eq!(
131 still_unmapped, 0,
132 "v3: {still_unmapped} accounts still unmapped (was {prev_unmapped})",
133 );
134
135 for (account_id, _) in frame_system::Account::<T>::iter() {
137 assert!(
138 T::Currency::balance_on_hold(
139 &crate::HoldReason::AddressMapping.into(),
140 &account_id,
141 )
142 .is_zero(),
143 "v3: account {account_id:?} still has address mapping deposit held",
144 );
145 }
146
147 Ok(())
148 }
149}
150
151#[test]
152fn migrate_to_v3() {
153 use crate::{
154 Config, OriginalAccount,
155 tests::{ExtBuilder, Test},
156 };
157 use frame_support::{traits::fungible::Mutate, weights::WeightMeter};
158 use sp_core::H160;
159 use sp_runtime::AccountId32;
160
161 ExtBuilder::default().genesis_config(None).build().execute_with(|| {
162 use crate::address::AccountId32Mapper;
163 use frame_support::traits::fungible::InspectHold;
164
165 let unmapped_accounts: Vec<AccountId32> =
166 (10..15u8).map(|i| AccountId32::new([i; 32])).collect();
167 let mapped_accounts: Vec<AccountId32> =
168 (15..20u8).map(|i| AccountId32::new([i; 32])).collect();
169 let eth_account = {
170 let mut bytes = [0xEE; 32];
171 bytes[..20].copy_from_slice(&[0xAA; 20]);
172 AccountId32::new(bytes)
173 };
174
175 for acc in unmapped_accounts.iter().chain(&mapped_accounts) {
177 <Test as Config>::Currency::set_balance(acc, 1_000_000);
178 }
179 <Test as Config>::Currency::set_balance(ð_account, 1_000_000);
180
181 for acc in &mapped_accounts {
183 AccountId32Mapper::<Test>::map(acc).unwrap();
184 assert!(
185 <Test as Config>::Currency::balance_on_hold(
186 &crate::HoldReason::AddressMapping.into(),
187 acc
188 ) > 0,
189 );
190 }
191
192 let mut cursor = None;
194 let mut weight_meter = WeightMeter::new();
195 while let Some(new_cursor) = Migration::<Test>::step(cursor, &mut weight_meter).unwrap() {
196 cursor = Some(new_cursor);
197 }
198
199 for acc in unmapped_accounts.iter().chain(&mapped_accounts) {
201 assert!(AccountId32Mapper::<Test>::is_mapped(acc));
202 let addr = AccountId32Mapper::<Test>::to_address(acc);
203 assert_eq!(OriginalAccount::<Test>::get(addr).as_ref(), Some(acc));
204 }
205
206 for acc in &mapped_accounts {
208 assert_eq!(
209 <Test as Config>::Currency::balance_on_hold(
210 &crate::HoldReason::AddressMapping.into(),
211 acc
212 ),
213 0,
214 );
215 }
216
217 let eth_addr = H160::from_slice(&[0xAA; 20]);
219 assert!(OriginalAccount::<Test>::get(eth_addr).is_none());
220 });
221}
222
223#[test]
224fn migrate_to_v3_maps_all_accounts() {
225 use crate::{
226 Config,
227 address::AccountId32Mapper,
228 tests::{ExtBuilder, Test},
229 };
230 use frame_support::{traits::fungible::Mutate, weights::WeightMeter};
231 use sp_runtime::AccountId32;
232
233 ExtBuilder::default().genesis_config(None).build().execute_with(|| {
234 let accounts: Vec<AccountId32> = (10..15u8).map(|i| AccountId32::new([i; 32])).collect();
235 for acc in &accounts {
236 <Test as Config>::Currency::set_balance(acc, 1_000_000);
237 AccountId32Mapper::<Test>::map(acc).unwrap();
238 }
239
240 for acc in &accounts {
242 AccountId32Mapper::<Test>::unmap(acc).unwrap();
243 assert!(!AccountId32Mapper::<Test>::is_mapped(acc));
244 }
245
246 let mut cursor = None;
248 let mut meter = WeightMeter::new();
249 while let Some(new_cursor) = Migration::<Test>::step(cursor, &mut meter).unwrap() {
250 cursor = Some(new_cursor);
251 }
252
253 for acc in &accounts {
254 assert!(
255 AccountId32Mapper::<Test>::is_mapped(acc),
256 "account should be mapped after migration"
257 );
258 }
259 });
260}