frame_support/traits/
schedule.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 and associated utilities for scheduling dispatchables in FRAME.
19
20#[allow(deprecated)]
21use super::PreimageProvider;
22use alloc::vec::Vec;
23use codec::{Codec, Decode, Encode, EncodeLike, MaxEncodedLen};
24use core::{fmt::Debug, result::Result};
25use scale_info::TypeInfo;
26use sp_runtime::{traits::Saturating, DispatchError, RuntimeDebug};
27
28/// Information relating to the period of a scheduled task. First item is the length of the
29/// period and the second is the number of times it should be executed in total before the task
30/// is considered finished and removed.
31pub type Period<BlockNumber> = (BlockNumber, u32);
32
33/// Priority with which a call is scheduled. It's just a linear amount with lowest values meaning
34/// higher priority.
35pub type Priority = u8;
36
37/// The dispatch time of a scheduled task.
38#[derive(Encode, Decode, Copy, Clone, PartialEq, Eq, RuntimeDebug, TypeInfo, MaxEncodedLen)]
39pub enum DispatchTime<BlockNumber> {
40	/// At specified block.
41	At(BlockNumber),
42	/// After specified number of blocks.
43	After(BlockNumber),
44}
45
46impl<BlockNumber: Saturating + Copy> DispatchTime<BlockNumber> {
47	pub fn evaluate(&self, since: BlockNumber) -> BlockNumber {
48		match &self {
49			Self::At(m) => *m,
50			Self::After(m) => m.saturating_add(since),
51		}
52	}
53}
54
55/// The highest priority. We invert the value so that normal sorting will place the highest
56/// priority at the beginning of the list.
57pub const HIGHEST_PRIORITY: Priority = 0;
58/// Anything of this value or lower will definitely be scheduled on the block that they ask for,
59/// even if it breaches the `MaximumWeight` limitation.
60pub const HARD_DEADLINE: Priority = 63;
61/// The lowest priority. Most stuff should be around here.
62pub const LOWEST_PRIORITY: Priority = 255;
63
64/// Type representing an encodable value or the hash of the encoding of such a value.
65#[derive(Clone, Eq, PartialEq, Encode, Decode, RuntimeDebug, TypeInfo, MaxEncodedLen)]
66pub enum MaybeHashed<T, Hash> {
67	/// The value itself.
68	Value(T),
69	/// The hash of the encoded value which this value represents.
70	Hash(Hash),
71}
72
73impl<T, H> From<T> for MaybeHashed<T, H> {
74	fn from(t: T) -> Self {
75		MaybeHashed::Value(t)
76	}
77}
78
79/// Error type for `MaybeHashed::lookup`.
80#[derive(Clone, Eq, PartialEq, Encode, Decode, RuntimeDebug, TypeInfo, MaxEncodedLen)]
81pub enum LookupError {
82	/// A call of this hash was not known.
83	Unknown,
84	/// The preimage for this hash was known but could not be decoded into a `Call`.
85	BadFormat,
86}
87
88impl<T: Decode, H> MaybeHashed<T, H> {
89	pub fn as_value(&self) -> Option<&T> {
90		match &self {
91			Self::Value(c) => Some(c),
92			Self::Hash(_) => None,
93		}
94	}
95
96	pub fn as_hash(&self) -> Option<&H> {
97		match &self {
98			Self::Value(_) => None,
99			Self::Hash(h) => Some(h),
100		}
101	}
102
103	pub fn ensure_requested<P: PreimageProvider<H>>(&self) {
104		match &self {
105			Self::Value(_) => (),
106			Self::Hash(hash) => P::request_preimage(hash),
107		}
108	}
109
110	pub fn ensure_unrequested<P: PreimageProvider<H>>(&self) {
111		match &self {
112			Self::Value(_) => (),
113			Self::Hash(hash) => P::unrequest_preimage(hash),
114		}
115	}
116
117	pub fn resolved<P: PreimageProvider<H>>(self) -> (Self, Option<H>) {
118		match self {
119			Self::Value(c) => (Self::Value(c), None),
120			Self::Hash(h) => {
121				let data = match P::get_preimage(&h) {
122					Some(p) => p,
123					None => return (Self::Hash(h), None),
124				};
125				match T::decode(&mut &data[..]) {
126					Ok(c) => (Self::Value(c), Some(h)),
127					Err(_) => (Self::Hash(h), None),
128				}
129			},
130		}
131	}
132}
133
134#[deprecated(note = "Use `v3` instead. Will be removed after September 2024.")]
135pub mod v1 {
136	use super::*;
137
138	/// A type that can be used as a scheduler.
139	pub trait Anon<BlockNumber, Call, RuntimeOrigin> {
140		/// An address which can be used for removing a scheduled task.
141		type Address: Codec + Clone + Eq + EncodeLike + Debug + TypeInfo + MaxEncodedLen;
142
143		/// Schedule a dispatch to happen at the beginning of some block in the future.
144		///
145		/// This is not named.
146		fn schedule(
147			when: DispatchTime<BlockNumber>,
148			maybe_periodic: Option<Period<BlockNumber>>,
149			priority: Priority,
150			origin: RuntimeOrigin,
151			call: Call,
152		) -> Result<Self::Address, DispatchError>;
153
154		/// Cancel a scheduled task. If periodic, then it will cancel all further instances of that,
155		/// also.
156		///
157		/// Will return an error if the `address` is invalid.
158		///
159		/// NOTE: This guaranteed to work only *before* the point that it is due to be executed.
160		/// If it ends up being delayed beyond the point of execution, then it cannot be cancelled.
161		///
162		/// NOTE2: This will not work to cancel periodic tasks after their initial execution. For
163		/// that, you must name the task explicitly using the `Named` trait.
164		fn cancel(address: Self::Address) -> Result<(), ()>;
165
166		/// Reschedule a task. For one-off tasks, this dispatch is guaranteed to succeed
167		/// only if it is executed *before* the currently scheduled block. For periodic tasks,
168		/// this dispatch is guaranteed to succeed only before the *initial* execution; for
169		/// others, use `reschedule_named`.
170		///
171		/// Will return an error if the `address` is invalid.
172		fn reschedule(
173			address: Self::Address,
174			when: DispatchTime<BlockNumber>,
175		) -> Result<Self::Address, DispatchError>;
176
177		/// Return the next dispatch time for a given task.
178		///
179		/// Will return an error if the `address` is invalid.
180		fn next_dispatch_time(address: Self::Address) -> Result<BlockNumber, ()>;
181	}
182
183	/// A type that can be used as a scheduler.
184	pub trait Named<BlockNumber, Call, RuntimeOrigin> {
185		/// An address which can be used for removing a scheduled task.
186		type Address: Codec + Clone + Eq + EncodeLike + core::fmt::Debug + MaxEncodedLen;
187
188		/// Schedule a dispatch to happen at the beginning of some block in the future.
189		///
190		/// - `id`: The identity of the task. This must be unique and will return an error if not.
191		fn schedule_named(
192			id: Vec<u8>,
193			when: DispatchTime<BlockNumber>,
194			maybe_periodic: Option<Period<BlockNumber>>,
195			priority: Priority,
196			origin: RuntimeOrigin,
197			call: Call,
198		) -> Result<Self::Address, ()>;
199
200		/// Cancel a scheduled, named task. If periodic, then it will cancel all further instances
201		/// of that, also.
202		///
203		/// Will return an error if the `id` is invalid.
204		///
205		/// NOTE: This guaranteed to work only *before* the point that it is due to be executed.
206		/// If it ends up being delayed beyond the point of execution, then it cannot be cancelled.
207		fn cancel_named(id: Vec<u8>) -> Result<(), ()>;
208
209		/// Reschedule a task. For one-off tasks, this dispatch is guaranteed to succeed
210		/// only if it is executed *before* the currently scheduled block.
211		fn reschedule_named(
212			id: Vec<u8>,
213			when: DispatchTime<BlockNumber>,
214		) -> Result<Self::Address, DispatchError>;
215
216		/// Return the next dispatch time for a given task.
217		///
218		/// Will return an error if the `id` is invalid.
219		fn next_dispatch_time(id: Vec<u8>) -> Result<BlockNumber, ()>;
220	}
221
222	#[allow(deprecated)]
223	impl<T, BlockNumber, Call, RuntimeOrigin> Anon<BlockNumber, Call, RuntimeOrigin> for T
224	where
225		T: v2::Anon<BlockNumber, Call, RuntimeOrigin>,
226	{
227		#[allow(deprecated)]
228		type Address = T::Address;
229
230		fn schedule(
231			when: DispatchTime<BlockNumber>,
232			maybe_periodic: Option<Period<BlockNumber>>,
233			priority: Priority,
234			origin: RuntimeOrigin,
235			call: Call,
236		) -> Result<Self::Address, DispatchError> {
237			let c = MaybeHashed::<Call, T::Hash>::Value(call);
238
239			#[allow(deprecated)]
240			T::schedule(when, maybe_periodic, priority, origin, c)
241		}
242
243		fn cancel(address: Self::Address) -> Result<(), ()> {
244			#[allow(deprecated)]
245			T::cancel(address)
246		}
247
248		fn reschedule(
249			address: Self::Address,
250			when: DispatchTime<BlockNumber>,
251		) -> Result<Self::Address, DispatchError> {
252			#[allow(deprecated)]
253			T::reschedule(address, when)
254		}
255
256		fn next_dispatch_time(address: Self::Address) -> Result<BlockNumber, ()> {
257			#[allow(deprecated)]
258			T::next_dispatch_time(address)
259		}
260	}
261
262	#[allow(deprecated)]
263	impl<T, BlockNumber, Call, RuntimeOrigin> Named<BlockNumber, Call, RuntimeOrigin> for T
264	where
265		T: v2::Named<BlockNumber, Call, RuntimeOrigin>,
266	{
267		#[allow(deprecated)]
268		type Address = T::Address;
269
270		fn schedule_named(
271			id: Vec<u8>,
272			when: DispatchTime<BlockNumber>,
273			maybe_periodic: Option<Period<BlockNumber>>,
274			priority: Priority,
275			origin: RuntimeOrigin,
276			call: Call,
277		) -> Result<Self::Address, ()> {
278			let c = MaybeHashed::<Call, T::Hash>::Value(call);
279			#[allow(deprecated)]
280			T::schedule_named(id, when, maybe_periodic, priority, origin, c)
281		}
282
283		fn cancel_named(id: Vec<u8>) -> Result<(), ()> {
284			#[allow(deprecated)]
285			T::cancel_named(id)
286		}
287
288		fn reschedule_named(
289			id: Vec<u8>,
290			when: DispatchTime<BlockNumber>,
291		) -> Result<Self::Address, DispatchError> {
292			#[allow(deprecated)]
293			T::reschedule_named(id, when)
294		}
295
296		fn next_dispatch_time(id: Vec<u8>) -> Result<BlockNumber, ()> {
297			#[allow(deprecated)]
298			T::next_dispatch_time(id)
299		}
300	}
301}
302
303#[deprecated(note = "Use `v3` instead. Will be removed after September 2024.")]
304pub mod v2 {
305	use super::*;
306
307	/// A type that can be used as a scheduler.
308	pub trait Anon<BlockNumber, Call, RuntimeOrigin> {
309		/// An address which can be used for removing a scheduled task.
310		type Address: Codec + Clone + Eq + EncodeLike + Debug + TypeInfo + MaxEncodedLen;
311		/// A means of expressing a call by the hash of its encoded data.
312		type Hash;
313
314		/// Schedule a dispatch to happen at the beginning of some block in the future.
315		///
316		/// This is not named.
317		fn schedule(
318			when: DispatchTime<BlockNumber>,
319			maybe_periodic: Option<Period<BlockNumber>>,
320			priority: Priority,
321			origin: RuntimeOrigin,
322			call: MaybeHashed<Call, Self::Hash>,
323		) -> Result<Self::Address, DispatchError>;
324
325		/// Cancel a scheduled task. If periodic, then it will cancel all further instances of that,
326		/// also.
327		///
328		/// Will return an error if the `address` is invalid.
329		///
330		/// NOTE: This guaranteed to work only *before* the point that it is due to be executed.
331		/// If it ends up being delayed beyond the point of execution, then it cannot be cancelled.
332		///
333		/// NOTE2: This will not work to cancel periodic tasks after their initial execution. For
334		/// that, you must name the task explicitly using the `Named` trait.
335		fn cancel(address: Self::Address) -> Result<(), ()>;
336
337		/// Reschedule a task. For one-off tasks, this dispatch is guaranteed to succeed
338		/// only if it is executed *before* the currently scheduled block. For periodic tasks,
339		/// this dispatch is guaranteed to succeed only before the *initial* execution; for
340		/// others, use `reschedule_named`.
341		///
342		/// Will return an error if the `address` is invalid.
343		fn reschedule(
344			address: Self::Address,
345			when: DispatchTime<BlockNumber>,
346		) -> Result<Self::Address, DispatchError>;
347
348		/// Return the next dispatch time for a given task.
349		///
350		/// Will return an error if the `address` is invalid.
351		fn next_dispatch_time(address: Self::Address) -> Result<BlockNumber, ()>;
352	}
353
354	/// A type that can be used as a scheduler.
355	pub trait Named<BlockNumber, Call, RuntimeOrigin> {
356		/// An address which can be used for removing a scheduled task.
357		type Address: Codec + Clone + Eq + EncodeLike + core::fmt::Debug + MaxEncodedLen;
358		/// A means of expressing a call by the hash of its encoded data.
359		type Hash;
360
361		/// Schedule a dispatch to happen at the beginning of some block in the future.
362		///
363		/// - `id`: The identity of the task. This must be unique and will return an error if not.
364		fn schedule_named(
365			id: Vec<u8>,
366			when: DispatchTime<BlockNumber>,
367			maybe_periodic: Option<Period<BlockNumber>>,
368			priority: Priority,
369			origin: RuntimeOrigin,
370			call: MaybeHashed<Call, Self::Hash>,
371		) -> Result<Self::Address, ()>;
372
373		/// Cancel a scheduled, named task. If periodic, then it will cancel all further instances
374		/// of that, also.
375		///
376		/// Will return an error if the `id` is invalid.
377		///
378		/// NOTE: This guaranteed to work only *before* the point that it is due to be executed.
379		/// If it ends up being delayed beyond the point of execution, then it cannot be cancelled.
380		fn cancel_named(id: Vec<u8>) -> Result<(), ()>;
381
382		/// Reschedule a task. For one-off tasks, this dispatch is guaranteed to succeed
383		/// only if it is executed *before* the currently scheduled block.
384		fn reschedule_named(
385			id: Vec<u8>,
386			when: DispatchTime<BlockNumber>,
387		) -> Result<Self::Address, DispatchError>;
388
389		/// Return the next dispatch time for a given task.
390		///
391		/// Will return an error if the `id` is invalid.
392		fn next_dispatch_time(id: Vec<u8>) -> Result<BlockNumber, ()>;
393	}
394}
395
396pub mod v3 {
397	use super::*;
398	use crate::traits::Bounded;
399
400	/// A type that can be used as a scheduler.
401	pub trait Anon<BlockNumber, Call, Origin> {
402		/// An address which can be used for removing a scheduled task.
403		type Address: Codec + MaxEncodedLen + Clone + Eq + EncodeLike + Debug + TypeInfo;
404		/// The hasher used in the runtime.
405		type Hasher: sp_runtime::traits::Hash;
406
407		/// Schedule a dispatch to happen at the beginning of some block in the future.
408		///
409		/// This is not named.
410		fn schedule(
411			when: DispatchTime<BlockNumber>,
412			maybe_periodic: Option<Period<BlockNumber>>,
413			priority: Priority,
414			origin: Origin,
415			call: Bounded<Call, Self::Hasher>,
416		) -> Result<Self::Address, DispatchError>;
417
418		/// Cancel a scheduled task. If periodic, then it will cancel all further instances of that,
419		/// also.
420		///
421		/// Will return an `Unavailable` error if the `address` is invalid.
422		///
423		/// NOTE: This guaranteed to work only *before* the point that it is due to be executed.
424		/// If it ends up being delayed beyond the point of execution, then it cannot be cancelled.
425		///
426		/// NOTE2: This will not work to cancel periodic tasks after their initial execution. For
427		/// that, you must name the task explicitly using the `Named` trait.
428		fn cancel(address: Self::Address) -> Result<(), DispatchError>;
429
430		/// Reschedule a task. For one-off tasks, this dispatch is guaranteed to succeed
431		/// only if it is executed *before* the currently scheduled block. For periodic tasks,
432		/// this dispatch is guaranteed to succeed only before the *initial* execution; for
433		/// others, use `reschedule_named`.
434		///
435		/// Will return an `Unavailable` error if the `address` is invalid.
436		fn reschedule(
437			address: Self::Address,
438			when: DispatchTime<BlockNumber>,
439		) -> Result<Self::Address, DispatchError>;
440
441		/// Return the next dispatch time for a given task.
442		///
443		/// Will return an `Unavailable` error if the `address` is invalid.
444		fn next_dispatch_time(address: Self::Address) -> Result<BlockNumber, DispatchError>;
445	}
446
447	pub type TaskName = [u8; 32];
448
449	/// A type that can be used as a scheduler.
450	pub trait Named<BlockNumber, Call, Origin> {
451		/// An address which can be used for removing a scheduled task.
452		type Address: Codec + MaxEncodedLen + Clone + Eq + EncodeLike + core::fmt::Debug;
453		/// The hasher used in the runtime.
454		type Hasher: sp_runtime::traits::Hash;
455
456		/// Schedule a dispatch to happen at the beginning of some block in the future.
457		///
458		/// - `id`: The identity of the task. This must be unique and will return an error if not.
459		///
460		/// NOTE: This will request `call` to be made available.
461		fn schedule_named(
462			id: TaskName,
463			when: DispatchTime<BlockNumber>,
464			maybe_periodic: Option<Period<BlockNumber>>,
465			priority: Priority,
466			origin: Origin,
467			call: Bounded<Call, Self::Hasher>,
468		) -> Result<Self::Address, DispatchError>;
469
470		/// Cancel a scheduled, named task. If periodic, then it will cancel all further instances
471		/// of that, also.
472		///
473		/// Will return an `Unavailable` error if the `id` is invalid.
474		///
475		/// NOTE: This guaranteed to work only *before* the point that it is due to be executed.
476		/// If it ends up being delayed beyond the point of execution, then it cannot be cancelled.
477		fn cancel_named(id: TaskName) -> Result<(), DispatchError>;
478
479		/// Reschedule a task. For one-off tasks, this dispatch is guaranteed to succeed
480		/// only if it is executed *before* the currently scheduled block.
481		///
482		/// Will return an `Unavailable` error if the `id` is invalid.
483		fn reschedule_named(
484			id: TaskName,
485			when: DispatchTime<BlockNumber>,
486		) -> Result<Self::Address, DispatchError>;
487
488		/// Return the next dispatch time for a given task.
489		///
490		/// Will return an `Unavailable` error if the `id` is invalid.
491		fn next_dispatch_time(id: TaskName) -> Result<BlockNumber, DispatchError>;
492	}
493}
494
495#[allow(deprecated)]
496pub use v1::*;