git.delta.rocks / unique-network / refs/commits / 5946798abe51

difftreelog

doc: scheduler

Daniel Shiposha2022-11-08parent: #61b47c8.patch.diff
in: master

1 file changed

modifiedpallets/scheduler-v2/src/lib.rsdiffbeforeafterboth
6666
67// Ensure we're `no_std` when compiling for Wasm.67// Ensure we're `no_std` when compiling for Wasm.
68#![cfg_attr(not(feature = "std"), no_std)]68#![cfg_attr(not(feature = "std"), no_std)]
69#![deny(missing_docs)]
6970
70#[cfg(feature = "runtime-benchmarks")]71#[cfg(feature = "runtime-benchmarks")]
71mod benchmarking;72mod benchmarking;
106/// The location of a scheduled task that can be used to remove it.107/// The location of a scheduled task that can be used to remove it.
107pub type TaskAddress<BlockNumber> = (BlockNumber, u32);108pub type TaskAddress<BlockNumber> = (BlockNumber, u32);
108109
110/// A an encoded bounded `Call`. Its encoding must be at most 128 bytes.
109pub type EncodedCall = BoundedVec<u8, ConstU32<128>>;111pub type EncodedCall = BoundedVec<u8, ConstU32<128>>;
110112
111#[derive(Clone, Eq, PartialEq, Encode, Decode, RuntimeDebug, TypeInfo, MaxEncodedLen)]113#[derive(Clone, Eq, PartialEq, Encode, Decode, RuntimeDebug, TypeInfo, MaxEncodedLen)]
112#[scale_info(skip_type_params(T))]114#[scale_info(skip_type_params(T))]
115/// A scheduled call is stored as is or as a preimage hash to lookup.
116/// This enum represents both variants.
113pub enum ScheduledCall<T: Config> {117pub enum ScheduledCall<T: Config> {
118 /// A an encoded bounded `Call`. Its encoding must be at most 128 bytes.
114 Inline(EncodedCall),119 Inline(EncodedCall),
120
121 /// A Blake2-256 hash of the call together with an upper limit for its size.
115 PreimageLookup { hash: T::Hash, unbounded_len: u32 },122 PreimageLookup {
123 /// A call hash to lookup
124 hash: T::Hash,
125
126 /// The length of the decoded call
127 unbounded_len: u32,
128 },
116}129}
117130
118impl<T: Config> ScheduledCall<T> {131impl<T: Config> ScheduledCall<T> {
132 /// Convert an otherwise unbounded or large value into a type ready for placing in storage.
133 ///
134 /// NOTE: Once this API is used, you should use either `drop` or `realize`.
119 pub fn new(call: <T as Config>::RuntimeCall) -> Result<Self, DispatchError> {135 pub fn new(call: <T as Config>::RuntimeCall) -> Result<Self, DispatchError> {
120 let encoded = call.encode();136 let encoded = call.encode();
121 let len = encoded.len();137 let len = encoded.len();
154 }170 }
155 }171 }
156172
173 // Decodes a runtime call
157 fn decode(mut data: &[u8]) -> Result<<T as Config>::RuntimeCall, DispatchError> {174 fn decode(mut data: &[u8]) -> Result<<T as Config>::RuntimeCall, DispatchError> {
158 <T as Config>::RuntimeCall::decode(&mut data)175 <T as Config>::RuntimeCall::decode(&mut data)
159 .map_err(|_| <Error<T>>::ScheduledCallCorrupted.into())176 .map_err(|_| <Error<T>>::ScheduledCallCorrupted.into())
160 }177 }
161}178}
162179
180/// A scheduler's interface for managing preimages to hashes
181/// and looking up preimages from their hash on-chain.
163pub trait SchedulerPreimages<T: Config>: PreimageRecipient<T::Hash> {182pub trait SchedulerPreimages<T: Config>: PreimageRecipient<T::Hash> {
183 /// No longer request that the data for decoding the given `call` is available.
164 fn drop(call: &ScheduledCall<T>);184 fn drop(call: &ScheduledCall<T>);
165185
186 /// Convert the given `call` instance back into its original instance, also returning the
187 /// exact size of its encoded form if it needed to be looked-up from a stored preimage.
188 ///
189 /// NOTE: This does not remove any data needed for realization. If you will no longer use the
190 /// `call`, use `realize` instead or use `drop` afterwards.
166 fn peek(191 fn peek(
167 call: &ScheduledCall<T>,192 call: &ScheduledCall<T>,
168 ) -> Result<(<T as pallet::Config>::RuntimeCall, Option<u32>), DispatchError>;193 ) -> Result<(<T as pallet::Config>::RuntimeCall, Option<u32>), DispatchError>;
210 }235 }
211}236}
212237
238/// Scheduler's supported origins.
213pub enum ScheduledEnsureOriginSuccess<AccountId> {239pub enum ScheduledEnsureOriginSuccess<AccountId> {
240 /// A scheduled transaction has the Root origin.
214 Root,241 Root,
242
243 /// A specific account has signed a scheduled transaction.
215 Signed(AccountId),244 Signed(AccountId),
216}245}
217246
247/// An identifier of a scheduled task.
218pub type TaskName = [u8; 32];248pub type TaskName = [u8; 32];
219249
220/// Information regarding an item to be executed in the future.250/// Information regarding an item to be executed in the future.
238 _phantom: PhantomData<AccountId>,268 _phantom: PhantomData<AccountId>,
239}269}
240270
271/// Information regarding an item to be executed in the future.
241pub type ScheduledOf<T> = Scheduled<272pub type ScheduledOf<T> = Scheduled<
242 TaskName,273 TaskName,
243 ScheduledCall<T>,274 ScheduledCall<T>,
248279
249#[derive(Encode, Decode, MaxEncodedLen, TypeInfo)]280#[derive(Encode, Decode, MaxEncodedLen, TypeInfo)]
250#[scale_info(skip_type_params(T))]281#[scale_info(skip_type_params(T))]
282/// A structure for storing scheduled tasks in a block.
283/// The `BlockAgenda` tracks the available free space for a new task in a block.4
284///
285/// The agenda's maximum amount of tasks is `T::MaxScheduledPerBlock`.
251pub struct BlockAgenda<T: Config> {286pub struct BlockAgenda<T: Config> {
252 agenda: BoundedVec<Option<ScheduledOf<T>>, T::MaxScheduledPerBlock>,287 agenda: BoundedVec<Option<ScheduledOf<T>>, T::MaxScheduledPerBlock>,
253 free_places: u32,288 free_places: u32,
254}289}
255290
256impl<T: Config> BlockAgenda<T> {291impl<T: Config> BlockAgenda<T> {
292 /// Tries to push a new scheduled task into the block's agenda.
293 /// If there is a free place, the new task will take it,
294 /// and the `BlockAgenda` will record that the number of free places has decreased.
295 ///
296 /// An error containing the scheduled task will be returned if there are no free places.
297 ///
298 /// The complexity of the check for the *existence* of a free place is O(1).
299 /// The complexity of *finding* the free slot is O(n).
257 fn try_push(&mut self, scheduled: ScheduledOf<T>) -> Result<u32, ScheduledOf<T>> {300 fn try_push(&mut self, scheduled: ScheduledOf<T>) -> Result<u32, ScheduledOf<T>> {
258 if self.free_places == 0 {301 if self.free_places == 0 {
259 return Err(scheduled);302 return Err(scheduled);
276 }319 }
277 }320 }
278321
322 /// Sets a slot by the given index and the slot value.
323 ///
324 /// ### Panics
325 /// If the index is out of range, the function will panic.
279 fn set_slot(&mut self, index: u32, slot: Option<ScheduledOf<T>>) {326 fn set_slot(&mut self, index: u32, slot: Option<ScheduledOf<T>>) {
280 self.agenda[index as usize] = slot;327 self.agenda[index as usize] = slot;
281 }328 }
282329
330 /// Returns an iterator containing references to the agenda's slots.
283 fn iter(&self) -> impl Iterator<Item = &'_ Option<ScheduledOf<T>>> + '_ {331 fn iter(&self) -> impl Iterator<Item = &'_ Option<ScheduledOf<T>>> + '_ {
284 self.agenda.iter()332 self.agenda.iter()
285 }333 }
286334
335 /// Returns an immutable reference to a scheduled task if there is one under the given index.
336 ///
337 /// The function returns `None` if:
338 /// * The `index` is out of range
339 /// * No scheduled task occupies the agenda slot under the given index.
287 fn get(&self, index: u32) -> Option<&ScheduledOf<T>> {340 fn get(&self, index: u32) -> Option<&ScheduledOf<T>> {
288 match self.agenda.get(index as usize) {341 match self.agenda.get(index as usize) {
289 Some(Some(scheduled)) => Some(scheduled),342 Some(Some(scheduled)) => Some(scheduled),
290 _ => None,343 _ => None,
291 }344 }
292 }345 }
293346
347 /// Returns a mutable reference to a scheduled task if there is one under the given index.
348 ///
349 /// The function returns `None` if:
350 /// * The `index` is out of range
351 /// * No scheduled task occupies the agenda slot under the given index.
294 fn get_mut(&mut self, index: u32) -> Option<&mut ScheduledOf<T>> {352 fn get_mut(&mut self, index: u32) -> Option<&mut ScheduledOf<T>> {
295 match self.agenda.get_mut(index as usize) {353 match self.agenda.get_mut(index as usize) {
296 Some(Some(scheduled)) => Some(scheduled),354 Some(Some(scheduled)) => Some(scheduled),
297 _ => None,355 _ => None,
298 }356 }
299 }357 }
300358
359 /// Take a scheduled task by the given index.
360 ///
361 /// If there is a task under the index, the function will:
362 /// * Free the corresponding agenda slot.
363 /// * Decrease the number of free places.
364 /// * Return the scheduled task.
365 ///
366 /// The function returns `None` if there is no task under the index.
301 fn take(&mut self, index: u32) -> Option<ScheduledOf<T>> {367 fn take(&mut self, index: u32) -> Option<ScheduledOf<T>> {
302 let removed = self.agenda.get_mut(index as usize)?.take();368 let removed = self.agenda.get_mut(index as usize)?.take();
303369
320 }386 }
321 }387 }
322}388}
323389/// A structure for tracking the used weight
390/// and checking if it does not exceed the weight limit.
324struct WeightCounter {391struct WeightCounter {
325 used: Weight,392 used: Weight,
326 limit: Weight,393 limit: Weight,
327}394}
328395
329impl WeightCounter {396impl WeightCounter {
397 /// Checks if the weight `w` can be accommodated by the counter.
398 ///
399 /// If there is room for the additional weight `w`,
400 /// the function will update the used weight and return true.
330 fn check_accrue(&mut self, w: Weight) -> bool {401 fn check_accrue(&mut self, w: Weight) -> bool {
331 let test = self.used.saturating_add(w);402 let test = self.used.saturating_add(w);
332 if test.any_gt(self.limit) {403 if test.any_gt(self.limit) {
337 }408 }
338 }409 }
339410
411 /// Checks if the weight `w` can be accommodated by the counter.
340 fn can_accrue(&mut self, w: Weight) -> bool {412 fn can_accrue(&mut self, w: Weight) -> bool {
341 self.used.saturating_add(w).all_lte(self.limit)413 self.used.saturating_add(w).all_lte(self.limit)
342 }414 }
343}415}
344416
345pub(crate) trait MarginalWeightInfo: WeightInfo {417pub(crate) trait MarginalWeightInfo: WeightInfo {
418 /// Return the weight of servicing a single task.
346 fn service_task(maybe_lookup_len: Option<usize>, named: bool, periodic: bool) -> Weight {419 fn service_task(maybe_lookup_len: Option<usize>, named: bool, periodic: bool) -> Weight {
347 let base = Self::service_task_base();420 let base = Self::service_task_base();
348 let mut total = match maybe_lookup_len {421 let mut total = match maybe_lookup_len {
381454
382 #[pallet::config]455 #[pallet::config]
383 pub trait Config: frame_system::Config {456 pub trait Config: frame_system::Config {
457 /// The overarching event type.
384 type RuntimeEvent: From<Event<Self>> + IsType<<Self as frame_system::Config>::RuntimeEvent>;458 type RuntimeEvent: From<Event<Self>> + IsType<<Self as frame_system::Config>::RuntimeEvent>;
385459
386 /// The aggregated origin which the dispatch will take.460 /// The aggregated origin which the dispatch will take.
442 type PrioritySetOrigin: EnsureOrigin<<Self as system::Config>::RuntimeOrigin>;516 type PrioritySetOrigin: EnsureOrigin<<Self as system::Config>::RuntimeOrigin>;
443 }517 }
444518
519 /// It contains the block number from which we should service tasks.
520 /// It's used for delaying the servicing of future blocks' agendas if we had overweight tasks.
445 #[pallet::storage]521 #[pallet::storage]
446 pub type IncompleteSince<T: Config> = StorageValue<_, T::BlockNumber>;522 pub type IncompleteSince<T: Config> = StorageValue<_, T::BlockNumber>;
447523
461 pub enum Event<T: Config> {537 pub enum Event<T: Config> {
462 /// Scheduled some task.538 /// Scheduled some task.
463 Scheduled { when: T::BlockNumber, index: u32 },539 Scheduled {
540 /// The block number in which the scheduled task should be executed.
541 when: T::BlockNumber,
542
543 /// The index of the block's agenda slot.
544 index: u32,
545 },
464 /// Canceled some task.546 /// Canceled some task.
465 Canceled { when: T::BlockNumber, index: u32 },547 Canceled {
548 /// The block number in which the canceled task has been.
549 when: T::BlockNumber,
550
551 /// The index of the block's agenda slot that had become available.
552 index: u32,
553 },
466 /// Dispatched some task.554 /// Dispatched some task.
467 Dispatched {555 Dispatched {
556 /// The task's address - the block number and the block's agenda index.
468 task: TaskAddress<T::BlockNumber>,557 task: TaskAddress<T::BlockNumber>,
558
559 /// The task's name if it is not anonymous.
469 id: Option<[u8; 32]>,560 id: Option<[u8; 32]>,
561
562 /// The task's execution result.
470 result: DispatchResult,563 result: DispatchResult,
471 },564 },
472 /// Scheduled task's priority has changed565 /// Scheduled task's priority has changed
473 PriorityChanged {566 PriorityChanged {
567 /// The task's address - the block number and the block's agenda index.
474 when: T::BlockNumber,568 task: TaskAddress<T::BlockNumber>,
475 index: u32,569
570 /// The new priority of the task.
476 priority: schedule::Priority,571 priority: schedule::Priority,
477 },572 },
478 /// The call for the provided hash was not found so the task has been aborted.573 /// The call for the provided hash was not found so the task has been aborted.
479 CallUnavailable {574 CallUnavailable {
575 /// The task's address - the block number and the block's agenda index.
480 task: TaskAddress<T::BlockNumber>,576 task: TaskAddress<T::BlockNumber>,
577
578 /// The task's name if it is not anonymous.
481 id: Option<[u8; 32]>,579 id: Option<[u8; 32]>,
482 },580 },
483 /// The given task can never be executed since it is overweight.581 /// The given task can never be executed since it is overweight.
484 PermanentlyOverweight {582 PermanentlyOverweight {
583 /// The task's address - the block number and the block's agenda index.
485 task: TaskAddress<T::BlockNumber>,584 task: TaskAddress<T::BlockNumber>,
585
586 /// The task's name if it is not anonymous.
486 id: Option<[u8; 32]>,587 id: Option<[u8; 32]>,
487 },588 },
488 }589 }
523 #[pallet::call]624 #[pallet::call]
524 impl<T: Config> Pallet<T> {625 impl<T: Config> Pallet<T> {
525 /// Anonymously schedule a task.626 /// Anonymously schedule a task.
627 ///
628 /// Only `T::ScheduleOrigin` is allowed to schedule a task.
629 /// Only `T::PrioritySetOrigin` is allowed to set the task's priority.
526 #[pallet::weight(<T as Config>::WeightInfo::schedule(T::MaxScheduledPerBlock::get()))]630 #[pallet::weight(<T as Config>::WeightInfo::schedule(T::MaxScheduledPerBlock::get()))]
527 pub fn schedule(631 pub fn schedule(
528 origin: OriginFor<T>,632 origin: OriginFor<T>,
549 }653 }
550654
551 /// Cancel an anonymously scheduled task.655 /// Cancel an anonymously scheduled task.
656 ///
657 /// The `T::OriginPrivilegeCmp` decides whether the given origin is allowed to cancel the task or not.
552 #[pallet::weight(<T as Config>::WeightInfo::cancel(T::MaxScheduledPerBlock::get()))]658 #[pallet::weight(<T as Config>::WeightInfo::cancel(T::MaxScheduledPerBlock::get()))]
553 pub fn cancel(origin: OriginFor<T>, when: T::BlockNumber, index: u32) -> DispatchResult {659 pub fn cancel(origin: OriginFor<T>, when: T::BlockNumber, index: u32) -> DispatchResult {
554 T::ScheduleOrigin::ensure_origin(origin.clone())?;660 T::ScheduleOrigin::ensure_origin(origin.clone())?;
558 }664 }
559665
560 /// Schedule a named task.666 /// Schedule a named task.
667 ///
668 /// Only `T::ScheduleOrigin` is allowed to schedule a task.
669 /// Only `T::PrioritySetOrigin` is allowed to set the task's priority.
561 #[pallet::weight(<T as Config>::WeightInfo::schedule_named(T::MaxScheduledPerBlock::get()))]670 #[pallet::weight(<T as Config>::WeightInfo::schedule_named(T::MaxScheduledPerBlock::get()))]
562 pub fn schedule_named(671 pub fn schedule_named(
563 origin: OriginFor<T>,672 origin: OriginFor<T>,
586 }695 }
587696
588 /// Cancel a named scheduled task.697 /// Cancel a named scheduled task.
698 ///
699 /// The `T::OriginPrivilegeCmp` decides whether the given origin is allowed to cancel the task or not.
589 #[pallet::weight(<T as Config>::WeightInfo::cancel_named(T::MaxScheduledPerBlock::get()))]700 #[pallet::weight(<T as Config>::WeightInfo::cancel_named(T::MaxScheduledPerBlock::get()))]
590 pub fn cancel_named(origin: OriginFor<T>, id: TaskName) -> DispatchResult {701 pub fn cancel_named(origin: OriginFor<T>, id: TaskName) -> DispatchResult {
591 T::ScheduleOrigin::ensure_origin(origin.clone())?;702 T::ScheduleOrigin::ensure_origin(origin.clone())?;
625 }736 }
626737
627 /// Schedule a named task after a delay.738 /// Schedule a named task after a delay.
739 ///
740 /// Only `T::ScheduleOrigin` is allowed to schedule a task.
741 /// Only `T::PrioritySetOrigin` is allowed to set the task's priority.
628 ///742 ///
629 /// # <weight>743 /// # <weight>
630 /// Same as [`schedule_named`](Self::schedule_named).744 /// Same as [`schedule_named`](Self::schedule_named).
656 Ok(())770 Ok(())
657 }771 }
658772
773 /// Change a named task's priority.
774 ///
775 /// Only the `T::PrioritySetOrigin` is allowed to change the task's priority.
659 #[pallet::weight(<T as Config>::WeightInfo::change_named_priority(T::MaxScheduledPerBlock::get()))]776 #[pallet::weight(<T as Config>::WeightInfo::change_named_priority(T::MaxScheduledPerBlock::get()))]
660 pub fn change_named_priority(777 pub fn change_named_priority(
661 origin: OriginFor<T>,778 origin: OriginFor<T>,
670}787}
671788
672impl<T: Config> Pallet<T> {789impl<T: Config> Pallet<T> {
790 /// Converts the `DispatchTime` to the `BlockNumber`.
791 ///
792 /// Returns an error if the block number is in the past.
673 fn resolve_time(when: DispatchTime<T::BlockNumber>) -> Result<T::BlockNumber, DispatchError> {793 fn resolve_time(when: DispatchTime<T::BlockNumber>) -> Result<T::BlockNumber, DispatchError> {
674 let now = frame_system::Pallet::<T>::block_number();794 let now = frame_system::Pallet::<T>::block_number();
675795
687 Ok(when)807 Ok(when)
688 }808 }
689809
810 /// Places the mandatory task.
811 ///
812 /// It will try to place the task into the block pointed by the `when` parameter.
813 ///
814 /// If the block has no room for a task,
815 /// the function will search for a future block that can accommodate the task.
690 fn mandatory_place_task(when: T::BlockNumber, what: ScheduledOf<T>) {816 fn mandatory_place_task(when: T::BlockNumber, what: ScheduledOf<T>) {
691 Self::place_task(when, what, true).expect("mandatory place task always succeeds; qed");817 Self::place_task(when, what, true).expect("mandatory place task always succeeds; qed");
692 }818 }
693819
820 /// Tries to place a task `what` into the given block `when`.
821 ///
822 /// Returns an error if the block has no room for the task.
694 fn try_place_task(823 fn try_place_task(
695 when: T::BlockNumber,824 when: T::BlockNumber,
696 what: ScheduledOf<T>,825 what: ScheduledOf<T>,
697 ) -> Result<TaskAddress<T::BlockNumber>, DispatchError> {826 ) -> Result<TaskAddress<T::BlockNumber>, DispatchError> {
698 Self::place_task(when, what, false)827 Self::place_task(when, what, false)
699 }828 }
700829
830 /// If `is_mandatory` is true, the function behaves like [`mandatory_place_task`](Self::mandatory_place_task);
831 /// otherwise it acts like [`try_place_task`](Self::try_place_task).
832 ///
833 /// The function also updates the `Lookup` storage.
701 fn place_task(834 fn place_task(
702 mut when: T::BlockNumber,835 mut when: T::BlockNumber,
703 what: ScheduledOf<T>,836 what: ScheduledOf<T>,
716 Ok(address)849 Ok(address)
717 }850 }
718851
852 /// Pushes the scheduled task into the block's agenda.
853 ///
854 /// If `is_mandatory` is true, it searches for a block with a free slot for the given task.
855 ///
856 /// If `is_mandatory` is false and there is no free slot, the function returns an error.
719 fn push_to_agenda(857 fn push_to_agenda(
720 when: &mut T::BlockNumber,858 when: &mut T::BlockNumber,
721 mut what: ScheduledOf<T>,859 mut what: ScheduledOf<T>,
8861024
887 scheduled.priority = priority;1025 scheduled.priority = priority;
888 Self::deposit_event(Event::PriorityChanged {1026 Self::deposit_event(Event::PriorityChanged {
889 when,1027 task: (when, index),
890 index,
891 priority,1028 priority,
892 });1029 });