pallet_assets_precompiles/
migration.rs1use crate::{foreign_assets::pallet, weights::WeightInfo};
21use core::marker::PhantomData;
22use frame_support::{
23 defensive,
24 migrations::{MigrationId, SteppedMigration, SteppedMigrationError},
25 weights::WeightMeter,
26};
27
28const PRECOMPILE_MAPPINGS_MIGRATION_ID: &[u8; 32] = b"foreign-asset-precompile-mapping";
29const LOG_TARGET: &str = "runtime::MigrateForeignAssetPrecompileMappings";
30
31pub struct MigrateForeignAssetPrecompileMappings<T, I, W>(PhantomData<(T, I, W)>);
83
84impl<T, I, W> SteppedMigration for MigrateForeignAssetPrecompileMappings<T, I, W>
85where
86 T: pallet_assets::Config<I>
87 + pallet::Config<ForeignAssetId = <T as pallet_assets::Config<I>>::AssetId>,
88 I: 'static,
89 W: WeightInfo,
90{
91 type Cursor = <T as pallet_assets::Config<I>>::AssetId;
92 type Identifier = MigrationId<32>;
93
94 fn id() -> Self::Identifier {
95 MigrationId { pallet_id: *PRECOMPILE_MAPPINGS_MIGRATION_ID, version_from: 0, version_to: 1 }
96 }
97
98 fn step(
99 cursor: Option<Self::Cursor>,
100 meter: &mut WeightMeter,
101 ) -> Result<Option<Self::Cursor>, SteppedMigrationError> {
102 let required = W::migrate_foreign_asset_step();
103 if meter.remaining().any_lt(required) {
104 return Err(SteppedMigrationError::InsufficientWeight { required });
105 }
106
107 let mut last_key = cursor;
108
109 loop {
110 if meter.try_consume(required).is_err() {
111 break;
112 }
113
114 let Some(asset_id) = Self::peek_next_asset(last_key.as_ref()) else {
115 log::info!(target: LOG_TARGET, "migration finished");
116 return Ok(None);
117 };
118
119 if pallet::Pallet::<T>::asset_index_of(&asset_id).is_none() {
120 match pallet::Pallet::<T>::insert_asset_mapping(&asset_id) {
121 Ok(asset_index) => log::debug!(
122 target: LOG_TARGET,
123 "Migrated asset {:?} to index {:?}",
124 asset_id,
125 asset_index
126 ),
127 Err(()) => {
128 defensive!("insert_asset_mapping failed during migration; this should be unreachable unless NextAssetIndex overflowed u32::MAX");
130 },
131 }
132 } else {
133 log::debug!(
134 target: LOG_TARGET,
135 "Skipping already-mapped asset {:?}",
136 asset_id
137 );
138 }
139 last_key = Some(asset_id);
140 }
141
142 Ok(last_key)
143 }
144
145 #[cfg(feature = "try-runtime")]
146 fn pre_upgrade() -> Result<alloc::vec::Vec<u8>, sp_runtime::TryRuntimeError> {
147 use codec::Encode;
148
149 let mut unmapped_assets = alloc::vec::Vec::new();
150
151 for (asset_id, _) in pallet_assets::Asset::<T, I>::iter() {
152 if pallet::Pallet::<T>::asset_index_of(&asset_id).is_none() {
153 unmapped_assets.push(asset_id);
154 }
155 }
156
157 log::info!(
158 target: LOG_TARGET,
159 "Found {} foreign assets needing migration",
160 unmapped_assets.len()
161 );
162
163 Ok(unmapped_assets.encode())
164 }
165
166 #[cfg(feature = "try-runtime")]
167 fn post_upgrade(state: alloc::vec::Vec<u8>) -> Result<(), sp_runtime::TryRuntimeError> {
168 use codec::Decode;
169
170 let unmapped_assets: alloc::vec::Vec<<T as pallet_assets::Config<I>>::AssetId> =
171 Decode::decode(&mut &state[..])
172 .map_err(|_| sp_runtime::TryRuntimeError::Other("Failed to decode state"))?;
173
174 let mut migrated = 0u64;
175
176 for asset_id in &unmapped_assets {
177 match pallet::Pallet::<T>::asset_index_of(asset_id) {
179 Some(index) => {
180 match pallet::Pallet::<T>::asset_id_of(index) {
182 Some(stored_id) if stored_id == *asset_id => {
183 migrated = migrated.saturating_add(1);
184 },
185 _ => {
186 return Err(sp_runtime::TryRuntimeError::Other(
187 "Reverse mapping mismatch",
188 ))
189 },
190 }
191 },
192 None => {
193 log::error!(
194 target: LOG_TARGET,
195 "Asset {:?} not migrated",
196 asset_id
197 );
198 return Err(sp_runtime::TryRuntimeError::Other("Asset not migrated"));
199 },
200 }
201 }
202
203 log::info!(
204 target: LOG_TARGET,
205 "Verified {} foreign asset mappings",
206 migrated
207 );
208
209 Ok(())
210 }
211}
212
213impl<T, I, W> MigrateForeignAssetPrecompileMappings<T, I, W>
214where
215 T: pallet_assets::Config<I>
216 + pallet::Config<ForeignAssetId = <T as pallet_assets::Config<I>>::AssetId>,
217 I: 'static,
218 W: WeightInfo,
219{
220 fn peek_next_asset(
223 maybe_last_key: Option<&<T as pallet_assets::Config<I>>::AssetId>,
224 ) -> Option<<T as pallet_assets::Config<I>>::AssetId> {
225 let mut iter = if let Some(last_key) = maybe_last_key {
226 pallet_assets::Asset::<T, I>::iter_keys_from(
227 pallet_assets::Asset::<T, I>::hashed_key_for(last_key),
228 )
229 } else {
230 pallet_assets::Asset::<T, I>::iter_keys()
231 };
232
233 iter.next()
234 }
235}