1use super::storage::Footprint;
10use crate::defensive;
11
12use alloc::vec::Vec;
13use codec::{Decode, DecodeWithMemTracking, Encode, FullCodec, MaxEncodedLen};
14use core::{cmp::Ordering, fmt::Debug, marker::PhantomData};
15use scale_info::TypeInfo;
16use subsoil::core::{ConstU32, Get, TypedGet};
17use subsoil::runtime::{traits::Convert, BoundedSlice};
18use subsoil::weights::{Weight, WeightMeter};
19
20#[derive(Copy, Clone, Eq, PartialEq, Encode, Decode, DecodeWithMemTracking, TypeInfo, Debug)]
23pub enum ProcessMessageError {
24 BadFormat,
26 Corrupt,
28 Unsupported,
30 Overweight(Weight),
34 Yield,
41 StackLimitReached,
43}
44
45pub trait ProcessMessage {
47 type Origin: FullCodec + MaxEncodedLen + Clone + Eq + PartialEq + TypeInfo + Debug;
49
50 fn process_message(
54 message: &[u8],
55 origin: Self::Origin,
56 meter: &mut WeightMeter,
57 id: &mut [u8; 32],
58 ) -> Result<bool, ProcessMessageError>;
59}
60
61#[derive(Eq, PartialEq, Debug)]
64pub enum ExecuteOverweightError {
65 NotFound,
67 AlreadyProcessed,
71 InsufficientWeight,
73 QueuePaused,
77 Other,
79 RecursiveDisallowed,
81}
82
83pub trait ServiceQueues {
85 type OverweightMessageAddress;
87
88 fn service_queues(weight_limit: Weight) -> Weight;
96
97 fn execute_overweight(
100 _weight_limit: Weight,
101 _address: Self::OverweightMessageAddress,
102 ) -> Result<Weight, ExecuteOverweightError> {
103 Err(ExecuteOverweightError::NotFound)
104 }
105}
106
107pub struct NoopServiceQueues<OverweightAddr>(PhantomData<OverweightAddr>);
109impl<OverweightAddr> ServiceQueues for NoopServiceQueues<OverweightAddr> {
110 type OverweightMessageAddress = OverweightAddr;
111
112 fn service_queues(_: Weight) -> Weight {
113 Weight::zero()
114 }
115}
116
117pub trait EnqueueMessage<Origin: MaxEncodedLen> {
119 type MaxMessageLen: Get<u32>;
121
122 fn enqueue_message(message: BoundedSlice<u8, Self::MaxMessageLen>, origin: Origin);
124
125 fn enqueue_messages<'a>(
127 messages: impl Iterator<Item = BoundedSlice<'a, u8, Self::MaxMessageLen>>,
128 origin: Origin,
129 );
130
131 fn sweep_queue(origin: Origin);
133}
134
135impl<Origin: MaxEncodedLen> EnqueueMessage<Origin> for () {
136 type MaxMessageLen = ConstU32<0>;
137 fn enqueue_message(_: BoundedSlice<u8, Self::MaxMessageLen>, _: Origin) {}
138 fn enqueue_messages<'a>(
139 _: impl Iterator<Item = BoundedSlice<'a, u8, Self::MaxMessageLen>>,
140 _: Origin,
141 ) {
142 }
143 fn sweep_queue(_: Origin) {}
144}
145
146#[derive(Default, Copy, Clone, Eq, PartialEq, Debug)]
148pub struct QueueFootprint {
149 pub pages: u32,
151 pub ready_pages: u32,
153 pub storage: Footprint,
155}
156
157#[derive(Default, Copy, Clone, PartialEq, Debug)]
159pub struct BatchFootprint {
160 pub msgs_count: usize,
162 pub size_in_bytes: usize,
164 pub new_pages_count: u32,
166}
167
168#[derive(Default, Debug)]
174pub struct BatchesFootprints {
175 pub first_page_pos: usize,
184 pub footprints: Vec<BatchFootprint>,
185}
186
187impl BatchesFootprints {
188 pub fn push(&mut self, msg: &[u8], new_page: bool) {
195 let previous_footprint =
196 self.footprints.last().map(|footprint| *footprint).unwrap_or_default();
197
198 let mut new_pages_count = previous_footprint.new_pages_count;
199 if new_page {
200 new_pages_count = new_pages_count.saturating_add(1);
201 }
202 self.footprints.push(BatchFootprint {
203 msgs_count: previous_footprint.msgs_count.saturating_add(1),
204 size_in_bytes: previous_footprint.size_in_bytes.saturating_add(msg.len()),
205 new_pages_count,
206 });
207 }
208
209 pub fn search_best_by<F>(&self, f: F) -> &BatchFootprint
211 where
212 F: FnMut(&BatchFootprint) -> Ordering,
213 {
214 let maybe_best_idx = match self.footprints.binary_search_by(f) {
216 Ok(last_ok_idx) => Some(last_ok_idx),
217 Err(first_err_idx) => first_err_idx.checked_sub(1),
218 };
219 if let Some(best_idx) = maybe_best_idx {
220 match self.footprints.get(best_idx) {
221 Some(best_footprint) => return best_footprint,
222 None => {
223 defensive!("Invalid best_batch_idx: {}", best_idx);
224 },
225 }
226 }
227 &BatchFootprint { msgs_count: 0, size_in_bytes: 0, new_pages_count: 0 }
228 }
229}
230
231pub trait QueueFootprintQuery<Origin> {
233 type MaxMessageLen: Get<u32>;
235
236 fn footprint(origin: Origin) -> QueueFootprint;
238
239 fn get_batches_footprints<'a>(
268 origin: Origin,
269 msgs: impl Iterator<Item = BoundedSlice<'a, u8, Self::MaxMessageLen>>,
270 total_pages_limit: u32,
271 ) -> BatchesFootprints;
272}
273
274impl<Origin: MaxEncodedLen> QueueFootprintQuery<Origin> for () {
275 type MaxMessageLen = ConstU32<0>;
276
277 fn footprint(_: Origin) -> QueueFootprint {
278 QueueFootprint::default()
279 }
280
281 fn get_batches_footprints<'a>(
282 _origin: Origin,
283 _msgs: impl Iterator<Item = BoundedSlice<'a, u8, Self::MaxMessageLen>>,
284 _total_pages_limit: u32,
285 ) -> BatchesFootprints {
286 BatchesFootprints::default()
287 }
288}
289
290pub struct TransformOrigin<E, O, N, C>(PhantomData<(E, O, N, C)>);
292impl<E: EnqueueMessage<O>, O: MaxEncodedLen, N: MaxEncodedLen, C: Convert<N, O>> EnqueueMessage<N>
293 for TransformOrigin<E, O, N, C>
294{
295 type MaxMessageLen = E::MaxMessageLen;
296
297 fn enqueue_message(message: BoundedSlice<u8, Self::MaxMessageLen>, origin: N) {
298 E::enqueue_message(message, C::convert(origin));
299 }
300
301 fn enqueue_messages<'a>(
302 messages: impl Iterator<Item = BoundedSlice<'a, u8, Self::MaxMessageLen>>,
303 origin: N,
304 ) {
305 E::enqueue_messages(messages, C::convert(origin));
306 }
307
308 fn sweep_queue(origin: N) {
309 E::sweep_queue(C::convert(origin));
310 }
311}
312
313impl<E: QueueFootprintQuery<O>, O: MaxEncodedLen, N: MaxEncodedLen, C: Convert<N, O>>
314 QueueFootprintQuery<N> for TransformOrigin<E, O, N, C>
315{
316 type MaxMessageLen = E::MaxMessageLen;
317
318 fn footprint(origin: N) -> QueueFootprint {
319 E::footprint(C::convert(origin))
320 }
321
322 fn get_batches_footprints<'a>(
323 origin: N,
324 msgs: impl Iterator<Item = BoundedSlice<'a, u8, Self::MaxMessageLen>>,
325 total_pages_limit: u32,
326 ) -> BatchesFootprints {
327 E::get_batches_footprints(C::convert(origin), msgs, total_pages_limit)
328 }
329}
330
331pub trait HandleMessage {
333 type MaxMessageLen: Get<u32>;
335
336 fn handle_message(message: BoundedSlice<u8, Self::MaxMessageLen>);
338
339 fn handle_messages<'a>(
341 messages: impl Iterator<Item = BoundedSlice<'a, u8, Self::MaxMessageLen>>,
342 );
343
344 fn sweep_queue();
346}
347
348pub struct EnqueueWithOrigin<E, O>(PhantomData<(E, O)>);
350impl<E: EnqueueMessage<O::Type>, O: TypedGet> HandleMessage for EnqueueWithOrigin<E, O>
351where
352 O::Type: MaxEncodedLen,
353{
354 type MaxMessageLen = E::MaxMessageLen;
355
356 fn handle_message(message: BoundedSlice<u8, Self::MaxMessageLen>) {
357 E::enqueue_message(message, O::get());
358 }
359
360 fn handle_messages<'a>(
361 messages: impl Iterator<Item = BoundedSlice<'a, u8, Self::MaxMessageLen>>,
362 ) {
363 E::enqueue_messages(messages, O::get());
364 }
365
366 fn sweep_queue() {
367 E::sweep_queue(O::get());
368 }
369}
370
371pub trait QueuePausedQuery<Origin> {
373 fn is_paused(origin: &Origin) -> bool;
375}
376
377#[impl_trait_for_tuples::impl_for_tuples(8)]
378impl<Origin> QueuePausedQuery<Origin> for Tuple {
379 fn is_paused(origin: &Origin) -> bool {
380 for_tuples!( #(
381 if Tuple::is_paused(origin) {
382 return true;
383 }
384 )* );
385 false
386 }
387}