polkadot_runtime_parachains/dmp/
inbound_downward_queue.rs1use super::*;
20
21use frame_support::traits::DefensiveSaturating;
22
23pub const LAZY_DELETE_MAX_PAGES: u32 = 3;
24
25pub struct InboundDownwardQueue<T>(pub core::marker::PhantomData<T>);
27impl<T: Config> InboundDownwardQueue<T> {
28 pub fn meta(para: ParaId) -> Option<InboundDownwardQueueMeta> {
30 DownwardMessageQueueMeta::<T>::get(para)
31 }
32
33 pub fn len(para: ParaId) -> Option<u64> {
35 let len_v0 = migration::v0::DownwardMessageQueues::<T>::decode_len(para);
36 let len_v1 = Self::meta(para)
37 .map(|meta| meta.first_free.defensive_saturating_sub(meta.first_full) as usize);
38
39 if len_v0.is_none() && len_v1.is_none() {
40 None
41 } else {
42 Some(len_v0.unwrap_or_default().saturating_add(len_v1.unwrap_or_default()) as u64)
43 }
44 }
45
46 pub fn push_back(
48 para: ParaId,
49 msg: DownwardMessage,
50 ) -> Result<InboundDownwardMessage<BlockNumberFor<T>>, ()> {
51 let inbound =
52 InboundDownwardMessage { sent_at: frame_system::Pallet::<T>::block_number(), msg };
53 Self::push_back_inbound(para, &inbound)?;
54 Ok(inbound)
55 }
56
57 pub fn push_back_inbound(
58 para: ParaId,
59 inbound: &InboundDownwardMessage<BlockNumberFor<T>>,
60 ) -> Result<(), ()> {
61 if migration::v0::DownwardMessageQueues::<T>::decode_len(para).is_some_and(|l| l > 0) {
63 migration::v0::DownwardMessageQueues::<T>::append(para, inbound);
64 return Ok(());
65 }
66
67 Self::push_back_inbound_v1(para, inbound)
68 }
69
70 pub fn push_back_inbound_v1(
72 para: ParaId,
73 inbound: &InboundDownwardMessage<BlockNumberFor<T>>,
74 ) -> Result<(), ()> {
75 let mut meta = Self::meta(para).unwrap_or_else(|| Self::new_meta(para));
76
77 let insert_location = meta.first_free;
78 meta.first_free = meta.first_free.checked_add(1).ok_or(())?;
79 DownwardMessageQueuePages::<T>::insert(para, insert_location, inbound);
80
81 DownwardMessageQueueMeta::<T>::insert(para, meta);
82
83 Ok(())
84 }
85
86 fn new_meta(para: ParaId) -> InboundDownwardQueueMeta {
90 let Some((_, last)) = DownwardMessageQueueLazyDelete::<T>::get(para) else {
91 return InboundDownwardQueueMeta { first_full: 0, first_free: 0 };
92 };
93
94 InboundDownwardQueueMeta { first_full: last, first_free: last }
95 }
96
97 #[cfg(test)]
99 pub fn pop_front(para: ParaId) -> Option<InboundDownwardMessage<BlockNumberFor<T>>> {
100 let Some(mut meta) = Self::meta(para) else {
102 return Self::pop_front_v0(para);
103 };
104
105 let front = DownwardMessageQueuePages::<T>::take(para, meta.first_full)?;
106 meta.first_full = meta.first_full.checked_add(1)?;
107 DownwardMessageQueueMeta::<T>::insert(para, meta);
108
109 Some(front)
110 }
111
112 pub fn pop_front_v0(para: ParaId) -> Option<InboundDownwardMessage<BlockNumberFor<T>>> {
113 let mut msgs = migration::v0::DownwardMessageQueues::<T>::get(para);
114 if msgs.is_empty() {
115 migration::v0::DownwardMessageQueues::<T>::remove(para); return None;
117 }
118
119 let front = msgs.remove(0); if msgs.is_empty() {
121 migration::v0::DownwardMessageQueues::<T>::remove(para);
122 } else {
123 migration::v0::DownwardMessageQueues::<T>::set(para, msgs);
124 }
125
126 Some(front)
127 }
128
129 pub fn peek_front(para: ParaId) -> Option<InboundDownwardMessage<BlockNumberFor<T>>> {
131 let front_v0 = || migration::v0::DownwardMessageQueues::<T>::get(para).first().cloned();
132
133 let front_v1 = Self::meta(para)
134 .and_then(|meta| DownwardMessageQueuePages::<T>::get(para, meta.first_full));
135
136 front_v1.or_else(front_v0)
137 }
138
139 pub fn drop_front_n(para: ParaId, mut n: u64) -> Option<u64> {
140 let dropped_v1 = Self::drop_front_n_v1(para, n);
142 n = n.saturating_sub(dropped_v1.unwrap_or_default());
143 let dropped_v0 = Self::drop_front_n_v0(para, n);
144
145 if dropped_v0.is_none() && dropped_v1.is_none() {
146 None
147 } else {
148 Some(dropped_v0.unwrap_or_default().saturating_add(dropped_v1.unwrap_or_default()))
149 }
150 }
151
152 fn drop_front_n_v1(para: ParaId, n: u64) -> Option<u64> {
156 let mut meta = Self::meta(para)?;
157
158 let old_first_full = meta.first_full;
159 meta.first_full = meta.first_full.saturating_add(n).min(meta.first_free);
160 DownwardMessageQueueMeta::<T>::insert(para, &meta);
161
162 let to_drop = meta.first_full.saturating_sub(old_first_full);
163 for i in old_first_full..meta.first_full {
164 DownwardMessageQueuePages::<T>::remove(para, i);
165 }
166
167 Some(to_drop)
168 }
169
170 fn drop_front_n_v0(para: ParaId, n: u64) -> Option<u64> {
171 if !migration::v0::DownwardMessageQueues::<T>::decode_len(para).is_some_and(|l| l > 0) {
172 return None;
173 }
174
175 let mut msgs = migration::v0::DownwardMessageQueues::<T>::get(para);
176 let take = n.min(msgs.len() as u64) as usize;
177 msgs.drain(..take);
178
179 if msgs.is_empty() {
180 migration::v0::DownwardMessageQueues::<T>::remove(para);
181 } else {
182 migration::v0::DownwardMessageQueues::<T>::set(para, msgs);
183 }
184
185 Some(take as u64)
186 }
187
188 pub fn delete_all(para: ParaId) {
190 migration::v0::DownwardMessageQueues::<T>::remove(para);
191
192 let Some(meta) = DownwardMessageQueueMeta::<T>::take(para) else {
193 return;
194 };
195 if meta.first_full >= meta.first_free {
196 return;
197 }
198
199 let cursor =
202 DownwardMessageQueuePages::<T>::clear_prefix(para, LAZY_DELETE_MAX_PAGES, None);
203
204 if cursor.maybe_cursor.is_none() {
205 return;
207 }
208
209 let (lo, hi) = match DownwardMessageQueueLazyDelete::<T>::get(para) {
210 Some((old_first, old_last)) => (old_first, meta.first_free.max(old_last)),
211 None => (meta.first_full, meta.first_free),
212 };
213 DownwardMessageQueueLazyDelete::<T>::insert(para, (lo, hi));
214 }
215
216 pub fn lazy_delete_some(weight_meter: &mut WeightMeter) {
218 if weight_meter.try_consume(<T as Config>::WeightInfo::lazy_delete_some()).is_err() {
219 return;
220 }
221
222 let Some((para_id, (first, last))) = DownwardMessageQueueLazyDelete::<T>::iter().next()
223 else {
224 return;
225 };
226
227 let mut next = first;
228 let end = next.saturating_add(LAZY_DELETE_MAX_PAGES as u64).min(last);
229 while next < end {
231 DownwardMessageQueuePages::<T>::remove(para_id, next);
232 next += 1;
233 }
234
235 if next >= last {
236 DownwardMessageQueueLazyDelete::<T>::remove(para_id);
237 } else {
238 DownwardMessageQueueLazyDelete::<T>::insert(para_id, (next, last));
239 }
240 }
241
242 pub fn peek_all_do_not_call_in_consensus(
244 para: ParaId,
245 ) -> Vec<InboundDownwardMessage<BlockNumberFor<T>>> {
246 let mut messages = Vec::new();
247
248 if let Some(meta) = Self::meta(para) {
250 for i in meta.first_full..meta.first_free {
251 if let Some(page) = DownwardMessageQueuePages::<T>::get(para, i).defensive() {
252 messages.push(page);
253 }
254 }
255 };
256
257 messages.extend(migration::v0::DownwardMessageQueues::<T>::get(para));
258
259 messages
260 }
261
262 #[cfg(any(feature = "std", feature = "try-runtime"))]
272 pub fn try_state() {
273 for (para, msgs) in migration::v0::DownwardMessageQueues::<T>::iter() {
274 assert!(!msgs.is_empty(), "v0 queue for {:?} is present but empty", para);
275 }
276 for (para, meta) in DownwardMessageQueueMeta::<T>::iter() {
277 assert!(
278 meta.first_full <= meta.first_free,
279 "meta for {:?} has first_full ({}) > first_free ({})",
280 para,
281 meta.first_full,
282 meta.first_free,
283 );
284 }
285 for (para, (first, last)) in DownwardMessageQueueLazyDelete::<T>::iter() {
286 assert!(
287 first <= last,
288 "lazy delete for {:?} has first ({}) > last ({})",
289 para,
290 first,
291 last,
292 );
293 }
294
295 for (para, idx) in DownwardMessageQueuePages::<T>::iter_keys() {
296 let in_meta = DownwardMessageQueueMeta::<T>::get(para)
297 .is_some_and(|m| idx >= m.first_full && idx < m.first_free);
298 let in_lazy = DownwardMessageQueueLazyDelete::<T>::get(para)
299 .is_some_and(|(first, last)| idx >= first && idx < last);
300
301 assert!(
302 in_meta || in_lazy,
303 "page ({:?}, {}) is orphaned: not covered by meta or lazy-delete range",
304 para,
305 idx,
306 );
307 }
308 }
309}