cumulus_pallet_dmp_queue/
lib.rs1#![cfg_attr(not(feature = "std"), no_std)]
24#![allow(deprecated)] extern crate alloc;
27
28use migration::*;
29pub use pallet::*;
30
31mod benchmarking;
32mod migration;
33mod mock;
34mod tests;
35pub mod weights;
36
37pub use weights::WeightInfo;
38
39pub type MaxDmpMessageLenOf<T> =
41 <<T as Config>::DmpSink as frame_support::traits::HandleMessage>::MaxMessageLen;
42
43#[frame_support::pallet]
44#[deprecated(
45 note = "`cumulus-pallet-dmp-queue` will be removed after November 2024. It can be removed once its lazy migration completed. See <https://github.com/paritytech/polkadot-sdk/pull/1246>."
46)]
47pub mod pallet {
48 use super::*;
49 use frame_support::{pallet_prelude::*, traits::HandleMessage, weights::WeightMeter};
50 use frame_system::pallet_prelude::*;
51 use sp_io::hashing::twox_128;
52
53 const STORAGE_VERSION: StorageVersion = StorageVersion::new(2);
54
55 #[pallet::pallet]
56 #[pallet::storage_version(STORAGE_VERSION)]
57 pub struct Pallet<T>(_);
58
59 #[pallet::config]
60 pub trait Config: frame_system::Config {
61 #[allow(deprecated)]
63 type RuntimeEvent: From<Event<Self>> + IsType<<Self as frame_system::Config>::RuntimeEvent>;
64
65 type DmpSink: HandleMessage;
67
68 type WeightInfo: WeightInfo;
70 }
71
72 #[pallet::storage]
74 pub type MigrationStatus<T> = StorageValue<_, MigrationState, ValueQuery>;
75
76 #[derive(
78 codec::Encode, codec::Decode, Debug, PartialEq, Eq, Clone, MaxEncodedLen, TypeInfo,
79 )]
80 pub enum MigrationState {
81 NotStarted,
83 StartedExport {
85 next_begin_used: PageCounter,
87 },
88 CompletedExport,
90 StartedOverweightExport {
92 next_overweight_index: u64,
94 },
95 CompletedOverweightExport,
97 StartedCleanup { cursor: Option<BoundedVec<u8, ConstU32<1024>>> },
99 Completed,
101 }
102
103 impl Default for MigrationState {
104 fn default() -> Self {
105 Self::NotStarted
106 }
107 }
108
109 #[pallet::event]
110 #[pallet::generate_deposit(pub(super) fn deposit_event)]
111 pub enum Event<T: Config> {
112 StartedExport,
114
115 Exported { page: PageCounter },
117
118 ExportFailed { page: PageCounter },
122
123 CompletedExport,
125
126 StartedOverweightExport,
128
129 ExportedOverweight { index: OverweightIndex },
131
132 ExportOverweightFailed { index: OverweightIndex },
136
137 CompletedOverweightExport,
139
140 StartedCleanup,
142
143 CleanedSome { keys_removed: u32 },
145
146 Completed { error: bool },
148 }
149
150 #[pallet::call]
151 impl<T: Config> Pallet<T> {}
152
153 #[pallet::hooks]
154 impl<T: Config> Hooks<BlockNumberFor<T>> for Pallet<T> {
155 fn integrity_test() {
156 let w = Self::on_idle_weight();
157 assert!(w != Weight::zero());
158 assert!(w.all_lte(T::BlockWeights::get().max_block));
159 }
160
161 fn on_idle(now: BlockNumberFor<T>, limit: Weight) -> Weight {
162 let mut meter = WeightMeter::with_limit(limit);
163
164 if meter.try_consume(Self::on_idle_weight()).is_err() {
165 log::debug!(target: LOG, "Not enough weight for on_idle. {} < {}", Self::on_idle_weight(), limit);
166 return meter.consumed()
167 }
168
169 let state = MigrationStatus::<T>::get();
170 let index = PageIndex::<T>::get();
171 log::debug!(target: LOG, "on_idle: block={:?}, state={:?}, index={:?}", now, state, index);
172
173 match state {
174 MigrationState::NotStarted => {
175 log::debug!(target: LOG, "Init export at page {}", index.begin_used);
176
177 MigrationStatus::<T>::put(MigrationState::StartedExport {
178 next_begin_used: index.begin_used,
179 });
180 Self::deposit_event(Event::StartedExport);
181 },
182 MigrationState::StartedExport { next_begin_used } => {
183 log::debug!(target: LOG, "Exporting page {}", next_begin_used);
184
185 if next_begin_used == index.end_used {
186 MigrationStatus::<T>::put(MigrationState::CompletedExport);
187 log::debug!(target: LOG, "CompletedExport");
188 Self::deposit_event(Event::CompletedExport);
189 } else {
190 let res = migration::migrate_page::<T>(next_begin_used);
191
192 MigrationStatus::<T>::put(MigrationState::StartedExport {
193 next_begin_used: next_begin_used.saturating_add(1),
194 });
195
196 if let Ok(()) = res {
197 log::debug!(target: LOG, "Exported page {}", next_begin_used);
198 Self::deposit_event(Event::Exported { page: next_begin_used });
199 } else {
200 Self::deposit_event(Event::ExportFailed { page: next_begin_used });
201 }
202 }
203 },
204 MigrationState::CompletedExport => {
205 log::debug!(target: LOG, "Init export overweight at index 0");
206
207 MigrationStatus::<T>::put(MigrationState::StartedOverweightExport {
208 next_overweight_index: 0,
209 });
210 Self::deposit_event(Event::StartedOverweightExport);
211 },
212 MigrationState::StartedOverweightExport { next_overweight_index } => {
213 log::debug!(target: LOG, "Exporting overweight index {}", next_overweight_index);
214
215 if next_overweight_index == index.overweight_count {
216 MigrationStatus::<T>::put(MigrationState::CompletedOverweightExport);
217 log::debug!(target: LOG, "CompletedOverweightExport");
218 Self::deposit_event(Event::CompletedOverweightExport);
219 } else {
220 let res = migration::migrate_overweight::<T>(next_overweight_index);
221
222 MigrationStatus::<T>::put(MigrationState::StartedOverweightExport {
223 next_overweight_index: next_overweight_index.saturating_add(1),
224 });
225
226 if let Ok(()) = res {
227 log::debug!(target: LOG, "Exported overweight index {next_overweight_index}");
228 Self::deposit_event(Event::ExportedOverweight {
229 index: next_overweight_index,
230 });
231 } else {
232 Self::deposit_event(Event::ExportOverweightFailed {
233 index: next_overweight_index,
234 });
235 }
236 }
237 },
238 MigrationState::CompletedOverweightExport => {
239 log::debug!(target: LOG, "Init cleanup");
240
241 MigrationStatus::<T>::put(MigrationState::StartedCleanup { cursor: None });
242 Self::deposit_event(Event::StartedCleanup);
243 },
244 MigrationState::StartedCleanup { cursor } => {
245 log::debug!(target: LOG, "Cleaning up");
246 let hashed_prefix =
247 twox_128(<Pallet<T> as PalletInfoAccess>::name().as_bytes());
248
249 let result = frame_support::storage::unhashed::clear_prefix(
250 &hashed_prefix,
251 Some(2), cursor.as_ref().map(|c| c.as_ref()),
253 );
254 Self::deposit_event(Event::CleanedSome { keys_removed: result.backend });
255
256 if let Some(unbound_cursor) = result.maybe_cursor {
259 if let Ok(cursor) = unbound_cursor.try_into() {
260 log::debug!(target: LOG, "Next cursor: {:?}", &cursor);
261 MigrationStatus::<T>::put(MigrationState::StartedCleanup {
262 cursor: Some(cursor),
263 });
264 } else {
265 MigrationStatus::<T>::put(MigrationState::Completed);
266 log::error!(target: LOG, "Completed with error: could not bound cursor");
267 Self::deposit_event(Event::Completed { error: true });
268 }
269 } else {
270 MigrationStatus::<T>::put(MigrationState::Completed);
271 log::debug!(target: LOG, "Completed");
272 Self::deposit_event(Event::Completed { error: false });
273 }
274 },
275 MigrationState::Completed => {
276 log::debug!(target: LOG, "Idle; you can remove this pallet");
277 },
278 }
279
280 meter.consumed()
281 }
282 }
283
284 impl<T: Config> Pallet<T> {
285 pub fn on_idle_weight() -> Weight {
287 <T as crate::Config>::WeightInfo::on_idle_good_msg()
288 .max(<T as crate::Config>::WeightInfo::on_idle_large_msg())
289 }
290 }
291}