frame_support/traits/
messages.rs

1// This file is part of Substrate.
2
3// Copyright (C) Parity Technologies (UK) Ltd.
4// SPDX-License-Identifier: Apache-2.0
5
6// Licensed under the Apache License, Version 2.0 (the "License");
7// you may not use this file except in compliance with the License.
8// You may obtain a copy of the License at
9//
10// 	http://www.apache.org/licenses/LICENSE-2.0
11//
12// Unless required by applicable law or agreed to in writing, software
13// distributed under the License is distributed on an "AS IS" BASIS,
14// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15// See the License for the specific language governing permissions and
16// limitations under the License.
17
18//! Traits for managing message queuing and handling.
19
20use super::storage::Footprint;
21use codec::{Decode, DecodeWithMemTracking, Encode, FullCodec, MaxEncodedLen};
22use core::{fmt::Debug, marker::PhantomData};
23use scale_info::TypeInfo;
24use sp_core::{ConstU32, Get, TypedGet};
25use sp_runtime::{traits::Convert, BoundedSlice, RuntimeDebug};
26use sp_weights::{Weight, WeightMeter};
27
28/// Errors that can happen when attempting to process a message with
29/// [`ProcessMessage::process_message()`].
30#[derive(Copy, Clone, Eq, PartialEq, Encode, Decode, DecodeWithMemTracking, TypeInfo, Debug)]
31pub enum ProcessMessageError {
32	/// The message data format is unknown (e.g. unrecognised header)
33	BadFormat,
34	/// The message data is bad (e.g. decoding returns an error).
35	Corrupt,
36	/// The message format is unsupported (e.g. old XCM version).
37	Unsupported,
38	/// Message processing was not attempted because it was not certain that the weight limit
39	/// would be respected. The parameter gives the maximum weight which the message could take
40	/// to process.
41	Overweight(Weight),
42	/// The queue wants to give up its current processing slot.
43	///
44	/// Hints the message processor to cease servicing this queue and proceed to the next
45	/// one. This is seen as a *hint*, not an instruction. Implementations must therefore handle
46	/// the case that a queue is re-serviced within the same block after *yielding*. A queue is
47	/// not required to *yield* again when it is being re-serviced withing the same block.
48	Yield,
49	/// The message could not be processed for reaching the stack depth limit.
50	StackLimitReached,
51}
52
53/// Can process messages from a specific origin.
54pub trait ProcessMessage {
55	/// The transport from where a message originates.
56	type Origin: FullCodec + MaxEncodedLen + Clone + Eq + PartialEq + TypeInfo + Debug;
57
58	/// Process the given message, using no more than the remaining `meter` weight to do so.
59	///
60	/// Returns whether the message was processed.
61	fn process_message(
62		message: &[u8],
63		origin: Self::Origin,
64		meter: &mut WeightMeter,
65		id: &mut [u8; 32],
66	) -> Result<bool, ProcessMessageError>;
67}
68
69/// Errors that can happen when attempting to execute an overweight message with
70/// [`ServiceQueues::execute_overweight()`].
71#[derive(Eq, PartialEq, RuntimeDebug)]
72pub enum ExecuteOverweightError {
73	/// The referenced message was not found.
74	NotFound,
75	/// The message was already processed.
76	///
77	/// This can be treated as success condition.
78	AlreadyProcessed,
79	/// The available weight was insufficient to execute the message.
80	InsufficientWeight,
81	/// The queue is paused and no message can be executed from it.
82	///
83	/// This can change at any time and may resolve in the future by re-trying.
84	QueuePaused,
85	/// An unspecified error.
86	Other,
87	/// Another call is currently ongoing and prevents this call from executing.
88	RecursiveDisallowed,
89}
90
91/// Can service queues and execute overweight messages.
92pub trait ServiceQueues {
93	/// Addresses a specific overweight message.
94	type OverweightMessageAddress;
95
96	/// Service all message queues in some fair manner.
97	///
98	/// - `weight_limit`: The maximum amount of dynamic weight that this call can use.
99	///
100	/// Returns the dynamic weight used by this call; is never greater than `weight_limit`.
101	/// Should only be called in top-level runtime entry points like `on_initialize` or `on_idle`.
102	/// Otherwise, stack depth limit errors may be miss-handled.
103	fn service_queues(weight_limit: Weight) -> Weight;
104
105	/// Executes a message that could not be executed by [`Self::service_queues()`] because it was
106	/// temporarily overweight.
107	fn execute_overweight(
108		_weight_limit: Weight,
109		_address: Self::OverweightMessageAddress,
110	) -> Result<Weight, ExecuteOverweightError> {
111		Err(ExecuteOverweightError::NotFound)
112	}
113}
114
115/// Services queues by doing nothing.
116pub struct NoopServiceQueues<OverweightAddr>(PhantomData<OverweightAddr>);
117impl<OverweightAddr> ServiceQueues for NoopServiceQueues<OverweightAddr> {
118	type OverweightMessageAddress = OverweightAddr;
119
120	fn service_queues(_: Weight) -> Weight {
121		Weight::zero()
122	}
123}
124
125/// The resource footprint of a queue.
126#[derive(Default, Copy, Clone, Eq, PartialEq, RuntimeDebug)]
127pub struct QueueFootprint {
128	/// The number of pages in the queue (including overweight pages).
129	pub pages: u32,
130	/// The number of pages that are ready (not yet processed and also not overweight).
131	pub ready_pages: u32,
132	/// The storage footprint of the queue (including overweight messages).
133	pub storage: Footprint,
134}
135
136/// Can enqueue messages for multiple origins.
137pub trait EnqueueMessage<Origin: MaxEncodedLen> {
138	/// The maximal length any enqueued message may have.
139	type MaxMessageLen: Get<u32>;
140
141	/// Enqueue a single `message` from a specific `origin`.
142	fn enqueue_message(message: BoundedSlice<u8, Self::MaxMessageLen>, origin: Origin);
143
144	/// Enqueue multiple `messages` from a specific `origin`.
145	fn enqueue_messages<'a>(
146		messages: impl Iterator<Item = BoundedSlice<'a, u8, Self::MaxMessageLen>>,
147		origin: Origin,
148	);
149
150	/// Any remaining unprocessed messages should happen only lazily, not proactively.
151	fn sweep_queue(origin: Origin);
152
153	/// Return the state footprint of the given queue.
154	fn footprint(origin: Origin) -> QueueFootprint;
155}
156
157impl<Origin: MaxEncodedLen> EnqueueMessage<Origin> for () {
158	type MaxMessageLen = ConstU32<0>;
159	fn enqueue_message(_: BoundedSlice<u8, Self::MaxMessageLen>, _: Origin) {}
160	fn enqueue_messages<'a>(
161		_: impl Iterator<Item = BoundedSlice<'a, u8, Self::MaxMessageLen>>,
162		_: Origin,
163	) {
164	}
165	fn sweep_queue(_: Origin) {}
166	fn footprint(_: Origin) -> QueueFootprint {
167		QueueFootprint::default()
168	}
169}
170
171/// Transform the origin of an [`EnqueueMessage`] via `C::convert`.
172pub struct TransformOrigin<E, O, N, C>(PhantomData<(E, O, N, C)>);
173impl<E: EnqueueMessage<O>, O: MaxEncodedLen, N: MaxEncodedLen, C: Convert<N, O>> EnqueueMessage<N>
174	for TransformOrigin<E, O, N, C>
175{
176	type MaxMessageLen = E::MaxMessageLen;
177
178	fn enqueue_message(message: BoundedSlice<u8, Self::MaxMessageLen>, origin: N) {
179		E::enqueue_message(message, C::convert(origin));
180	}
181
182	fn enqueue_messages<'a>(
183		messages: impl Iterator<Item = BoundedSlice<'a, u8, Self::MaxMessageLen>>,
184		origin: N,
185	) {
186		E::enqueue_messages(messages, C::convert(origin));
187	}
188
189	fn sweep_queue(origin: N) {
190		E::sweep_queue(C::convert(origin));
191	}
192
193	fn footprint(origin: N) -> QueueFootprint {
194		E::footprint(C::convert(origin))
195	}
196}
197
198/// Handles incoming messages for a single origin.
199pub trait HandleMessage {
200	/// The maximal length any enqueued message may have.
201	type MaxMessageLen: Get<u32>;
202
203	/// Enqueue a single `message` with an implied origin.
204	fn handle_message(message: BoundedSlice<u8, Self::MaxMessageLen>);
205
206	/// Enqueue multiple `messages` from an implied origin.
207	fn handle_messages<'a>(
208		messages: impl Iterator<Item = BoundedSlice<'a, u8, Self::MaxMessageLen>>,
209	);
210
211	/// Any remaining unprocessed messages should happen only lazily, not proactively.
212	fn sweep_queue();
213
214	/// Return the state footprint of the queue.
215	fn footprint() -> QueueFootprint;
216}
217
218/// Adapter type to transform an [`EnqueueMessage`] with an origin into a [`HandleMessage`] impl.
219pub struct EnqueueWithOrigin<E, O>(PhantomData<(E, O)>);
220impl<E: EnqueueMessage<O::Type>, O: TypedGet> HandleMessage for EnqueueWithOrigin<E, O>
221where
222	O::Type: MaxEncodedLen,
223{
224	type MaxMessageLen = E::MaxMessageLen;
225
226	fn handle_message(message: BoundedSlice<u8, Self::MaxMessageLen>) {
227		E::enqueue_message(message, O::get());
228	}
229
230	fn handle_messages<'a>(
231		messages: impl Iterator<Item = BoundedSlice<'a, u8, Self::MaxMessageLen>>,
232	) {
233		E::enqueue_messages(messages, O::get());
234	}
235
236	fn sweep_queue() {
237		E::sweep_queue(O::get());
238	}
239
240	fn footprint() -> QueueFootprint {
241		E::footprint(O::get())
242	}
243}
244
245/// Provides information on paused queues.
246pub trait QueuePausedQuery<Origin> {
247	/// Whether this queue is paused.
248	fn is_paused(origin: &Origin) -> bool;
249}
250
251#[impl_trait_for_tuples::impl_for_tuples(8)]
252impl<Origin> QueuePausedQuery<Origin> for Tuple {
253	fn is_paused(origin: &Origin) -> bool {
254		for_tuples!( #(
255			if Tuple::is_paused(origin) {
256				return true;
257			}
258		)* );
259		false
260	}
261}