difftreelog
fix make schedulerv2 take fees
in: master
3 files changed
pallets/scheduler-v2/src/lib.rsdiffbeforeafterboth1// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.2// This file is part of Unique Network.34// Unique Network is free software: you can redistribute it and/or modify5// it under the terms of the GNU General Public License as published by6// the Free Software Foundation, either version 3 of the License, or7// (at your option) any later version.89// Unique Network is distributed in the hope that it will be useful,10// but WITHOUT ANY WARRANTY; without even the implied warranty of11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the12// GNU General Public License for more details.1314// You should have received a copy of the GNU General Public License15// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.1617// Original license:18// This file is part of Substrate.1920// Copyright (C) 2017-2022 Parity Technologies (UK) Ltd.21// SPDX-License-Identifier: Apache-2.02223// Licensed under the Apache License, Version 2.0 (the "License");24// you may not use this file except in compliance with the License.25// You may obtain a copy of the License at26//27// http://www.apache.org/licenses/LICENSE-2.028//29// Unless required by applicable law or agreed to in writing, software30// distributed under the License is distributed on an "AS IS" BASIS,31// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.32// See the License for the specific language governing permissions and33// limitations under the License.3435//! # Scheduler36//! A Pallet for scheduling dispatches.37//!38//! - [`Config`]39//! - [`Call`]40//! - [`Pallet`]41//!42//! ## Overview43//!44//! This Pallet exposes capabilities for scheduling dispatches to occur at a45//! specified block number or at a specified period. These scheduled dispatches46//! may be named or anonymous and may be canceled.47//!48//! **NOTE:** The scheduled calls will be dispatched with the default filter49//! for the origin: namely `frame_system::Config::BaseCallFilter` for all origin50//! except root which will get no filter. And not the filter contained in origin51//! use to call `fn schedule`.52//!53//! If a call is scheduled using proxy or whatever mecanism which adds filter,54//! then those filter will not be used when dispatching the schedule call.55//!56//! ## Interface57//!58//! ### Dispatchable Functions59//!60//! * `schedule` - schedule a dispatch, which may be periodic, to occur at a specified block and61//! with a specified priority.62//! * `cancel` - cancel a scheduled dispatch, specified by block number and index.63//! * `schedule_named` - augments the `schedule` interface with an additional `Vec<u8>` parameter64//! that can be used for identification.65//! * `cancel_named` - the named complement to the cancel function.6667// Ensure we're `no_std` when compiling for Wasm.68#![cfg_attr(not(feature = "std"), no_std)]6970#[cfg(feature = "runtime-benchmarks")]71mod benchmarking;72#[cfg(test)]73mod mock;74#[cfg(test)]75mod tests;76pub mod weights;7778use codec::{Codec, Decode, Encode, MaxEncodedLen};79use frame_support::{80 dispatch::{DispatchError, DispatchResult, Dispatchable, GetDispatchInfo, Parameter},81 traits::{82 schedule::{self, DispatchTime},83 EnsureOrigin, Get, IsType, OriginTrait, PrivilegeCmp, StorageVersion, PreimageRecipient,84 ConstU32,85 },86 weights::Weight,87};8889use frame_system::{self as system};90use scale_info::TypeInfo;91use sp_runtime::{92 traits::{BadOrigin, One, Saturating, Zero, Hash},93 BoundedVec, RuntimeDebug,94};95use sp_std::{borrow::Borrow, cmp::Ordering, marker::PhantomData, prelude::*};96pub use weights::WeightInfo;9798pub use pallet::*;99100/// Just a simple index for naming period tasks.101pub type PeriodicIndex = u32;102/// The location of a scheduled task that can be used to remove it.103pub type TaskAddress<BlockNumber> = (BlockNumber, u32);104105pub type EncodedCall = BoundedVec<u8, ConstU32<128>>;106107#[derive(Clone, Eq, PartialEq, Encode, Decode, RuntimeDebug, TypeInfo, MaxEncodedLen)]108#[scale_info(skip_type_params(T))]109pub enum ScheduledCall<T: Config> {110 Inline(EncodedCall),111 PreimageLookup { hash: T::Hash, unbounded_len: u32 },112}113114impl<T: Config> ScheduledCall<T> {115 pub fn new(call: <T as Config>::Call) -> Result<Self, DispatchError> {116 let encoded = call.encode();117 let len = encoded.len();118119 match EncodedCall::try_from(encoded.clone()) {120 Ok(bounded) => Ok(Self::Inline(bounded)),121 Err(_) => {122 let hash = <T as system::Config>::Hashing::hash_of(&encoded);123 <T as Config>::Preimages::note_preimage(124 encoded125 .try_into()126 .map_err(|_| <Error<T>>::TooBigScheduledCall)?,127 );128129 Ok(Self::PreimageLookup {130 hash,131 unbounded_len: len as u32,132 })133 }134 }135 }136137 /// The maximum length of the lookup that is needed to peek `Self`.138 pub fn lookup_len(&self) -> Option<u32> {139 match self {140 Self::Inline(..) => None,141 Self::PreimageLookup { unbounded_len, .. } => Some(*unbounded_len),142 }143 }144145 /// Returns whether the image will require a lookup to be peeked.146 pub fn lookup_needed(&self) -> bool {147 match self {148 Self::Inline(_) => false,149 Self::PreimageLookup { .. } => true,150 }151 }152153 fn decode(mut data: &[u8]) -> Result<<T as Config>::Call, DispatchError> {154 <T as Config>::Call::decode(&mut data)155 .map_err(|_| <Error<T>>::ScheduledCallCorrupted.into())156 }157}158159pub trait SchedulerPreimages<T: Config>: PreimageRecipient<T::Hash> {160 fn drop(call: &ScheduledCall<T>);161162 fn peek(163 call: &ScheduledCall<T>,164 ) -> Result<(<T as pallet::Config>::Call, Option<u32>), DispatchError>;165166 /// Convert the given scheduled `call` value back into its original instance. If successful,167 /// `drop` any data backing it. This will not break the realisability of independently168 /// created instances of `ScheduledCall` which happen to have identical data.169 fn realize(170 call: &ScheduledCall<T>,171 ) -> Result<(<T as pallet::Config>::Call, Option<u32>), DispatchError>;172}173174impl<T: Config, PP: PreimageRecipient<T::Hash>> SchedulerPreimages<T> for PP {175 fn drop(call: &ScheduledCall<T>) {176 match call {177 ScheduledCall::Inline(_) => {}178 ScheduledCall::PreimageLookup { hash, .. } => Self::unrequest_preimage(hash),179 }180 }181182 fn peek(183 call: &ScheduledCall<T>,184 ) -> Result<(<T as pallet::Config>::Call, Option<u32>), DispatchError> {185 match call {186 ScheduledCall::Inline(data) => Ok((ScheduledCall::<T>::decode(data)?, None)),187 ScheduledCall::PreimageLookup {188 hash,189 unbounded_len,190 } => {191 let (preimage, len) = Self::get_preimage(hash)192 .ok_or(<Error<T>>::PreimageNotFound)193 .map(|preimage| (preimage, *unbounded_len))?;194195 Ok((ScheduledCall::<T>::decode(preimage.as_slice())?, Some(len)))196 }197 }198 }199200 fn realize(201 call: &ScheduledCall<T>,202 ) -> Result<(<T as pallet::Config>::Call, Option<u32>), DispatchError> {203 let r = Self::peek(call)?;204 Self::drop(call);205 Ok(r)206 }207}208209pub type TaskName = [u8; 32];210211/// Information regarding an item to be executed in the future.212#[cfg_attr(any(feature = "std", test), derive(PartialEq, Eq))]213#[derive(Clone, RuntimeDebug, Encode, Decode, MaxEncodedLen, TypeInfo)]214pub struct Scheduled<Name, Call, BlockNumber, PalletsOrigin, AccountId> {215 /// The unique identity for this task, if there is one.216 maybe_id: Option<Name>,217218 /// This task's priority.219 priority: schedule::Priority,220221 /// The call to be dispatched.222 call: Call,223224 /// If the call is periodic, then this points to the information concerning that.225 maybe_periodic: Option<schedule::Period<BlockNumber>>,226227 /// The origin with which to dispatch the call.228 origin: PalletsOrigin,229 _phantom: PhantomData<AccountId>,230}231232pub type ScheduledOf<T> = Scheduled<233 TaskName,234 ScheduledCall<T>,235 <T as frame_system::Config>::BlockNumber,236 <T as Config>::PalletsOrigin,237 <T as frame_system::Config>::AccountId,238>;239240struct WeightCounter {241 used: Weight,242 limit: Weight,243}244245impl WeightCounter {246 fn check_accrue(&mut self, w: Weight) -> bool {247 let test = self.used.saturating_add(w);248 if test > self.limit {249 false250 } else {251 self.used = test;252 true253 }254 }255256 fn can_accrue(&mut self, w: Weight) -> bool {257 self.used.saturating_add(w) <= self.limit258 }259}260261pub(crate) trait MarginalWeightInfo: WeightInfo {262 fn service_task(maybe_lookup_len: Option<usize>, named: bool, periodic: bool) -> Weight {263 let base = Self::service_task_base();264 let mut total = match maybe_lookup_len {265 None => base,266 Some(l) => Self::service_task_fetched(l as u32),267 };268 if named {269 total.saturating_accrue(Self::service_task_named().saturating_sub(base));270 }271 if periodic {272 total.saturating_accrue(Self::service_task_periodic().saturating_sub(base));273 }274 total275 }276}277278impl<T: WeightInfo> MarginalWeightInfo for T {}279280#[frame_support::pallet]281pub mod pallet {282 use super::*;283 use frame_support::{dispatch::PostDispatchInfo, pallet_prelude::*};284 use system::pallet_prelude::*;285286 /// The current storage version.287 const STORAGE_VERSION: StorageVersion = StorageVersion::new(0);288289 #[pallet::pallet]290 #[pallet::generate_store(pub(super) trait Store)]291 #[pallet::storage_version(STORAGE_VERSION)]292 pub struct Pallet<T>(_);293294 #[pallet::config]295 pub trait Config: frame_system::Config {296 type Event: From<Event<Self>> + IsType<<Self as frame_system::Config>::Event>;297298 /// The aggregated origin which the dispatch will take.299 type Origin: OriginTrait<PalletsOrigin = Self::PalletsOrigin>300 + From<Self::PalletsOrigin>301 + IsType<<Self as system::Config>::Origin>302 + Clone;303304 /// The caller origin, overarching type of all pallets origins.305 type PalletsOrigin: From<system::RawOrigin<Self::AccountId>>306 + Codec307 + Clone308 + Eq309 + TypeInfo310 + MaxEncodedLen;311312 /// The aggregated call type.313 type Call: Parameter314 + Dispatchable<Origin = <Self as Config>::Origin, PostInfo = PostDispatchInfo>315 + GetDispatchInfo316 + From<system::Call<Self>>;317318 /// The maximum weight that may be scheduled per block for any dispatchables.319 #[pallet::constant]320 type MaximumWeight: Get<Weight>;321322 /// Required origin to schedule or cancel calls.323 type ScheduleOrigin: EnsureOrigin<<Self as system::Config>::Origin>;324325 /// Compare the privileges of origins.326 ///327 /// This will be used when canceling a task, to ensure that the origin that tries328 /// to cancel has greater or equal privileges as the origin that created the scheduled task.329 ///330 /// For simplicity the [`EqualPrivilegeOnly`](frame_support::traits::EqualPrivilegeOnly) can331 /// be used. This will only check if two given origins are equal.332 type OriginPrivilegeCmp: PrivilegeCmp<Self::PalletsOrigin>;333334 /// The maximum number of scheduled calls in the queue for a single block.335 #[pallet::constant]336 type MaxScheduledPerBlock: Get<u32>;337338 /// Weight information for extrinsics in this pallet.339 type WeightInfo: WeightInfo;340341 /// The preimage provider with which we look up call hashes to get the call.342 type Preimages: SchedulerPreimages<Self>;343 }344345 #[pallet::storage]346 pub type IncompleteSince<T: Config> = StorageValue<_, T::BlockNumber>;347348 /// Items to be executed, indexed by the block number that they should be executed on.349 #[pallet::storage]350 pub type Agenda<T: Config> = StorageMap<351 _,352 Twox64Concat,353 T::BlockNumber,354 BoundedVec<Option<ScheduledOf<T>>, T::MaxScheduledPerBlock>,355 ValueQuery,356 >;357358 /// Lookup from a name to the block number and index of the task.359 #[pallet::storage]360 pub(crate) type Lookup<T: Config> =361 StorageMap<_, Twox64Concat, TaskName, TaskAddress<T::BlockNumber>>;362363 /// Events type.364 #[pallet::event]365 #[pallet::generate_deposit(pub(super) fn deposit_event)]366 pub enum Event<T: Config> {367 /// Scheduled some task.368 Scheduled { when: T::BlockNumber, index: u32 },369 /// Canceled some task.370 Canceled { when: T::BlockNumber, index: u32 },371 /// Dispatched some task.372 Dispatched {373 task: TaskAddress<T::BlockNumber>,374 id: Option<[u8; 32]>,375 result: DispatchResult,376 },377 /// The call for the provided hash was not found so the task has been aborted.378 CallUnavailable {379 task: TaskAddress<T::BlockNumber>,380 id: Option<[u8; 32]>,381 },382 /// The given task was unable to be renewed since the agenda is full at that block.383 PeriodicFailed {384 task: TaskAddress<T::BlockNumber>,385 id: Option<[u8; 32]>,386 },387 /// The given task can never be executed since it is overweight.388 PermanentlyOverweight {389 task: TaskAddress<T::BlockNumber>,390 id: Option<[u8; 32]>,391 },392 }393394 #[pallet::error]395 pub enum Error<T> {396 /// Failed to schedule a call397 FailedToSchedule,398 /// There is no place for a new task in the agenda399 AgendaIsExhausted,400 /// Scheduled call is corrupted401 ScheduledCallCorrupted,402 /// Scheduled call preimage is not found403 PreimageNotFound,404 /// Scheduled call is too big405 TooBigScheduledCall,406 /// Cannot find the scheduled call.407 NotFound,408 /// Given target block number is in the past.409 TargetBlockNumberInPast,410 /// Reschedule failed because it does not change scheduled time.411 RescheduleNoChange,412 /// Attempt to use a non-named function on a named task.413 Named,414 }415416 #[pallet::hooks]417 impl<T: Config> Hooks<BlockNumberFor<T>> for Pallet<T> {418 /// Execute the scheduled calls419 fn on_initialize(now: T::BlockNumber) -> Weight {420 let mut weight_counter = WeightCounter {421 used: Weight::zero(),422 limit: T::MaximumWeight::get(),423 };424 Self::service_agendas(&mut weight_counter, now, u32::max_value());425 weight_counter.used426 }427 }428429 #[pallet::call]430 impl<T: Config> Pallet<T> {431 /// Anonymously schedule a task.432 #[pallet::weight(<T as Config>::WeightInfo::schedule(T::MaxScheduledPerBlock::get()))]433 pub fn schedule(434 origin: OriginFor<T>,435 when: T::BlockNumber,436 maybe_periodic: Option<schedule::Period<T::BlockNumber>>,437 priority: schedule::Priority,438 call: Box<<T as Config>::Call>,439 ) -> DispatchResult {440 T::ScheduleOrigin::ensure_origin(origin.clone())?;441 let origin = <T as Config>::Origin::from(origin);442 Self::do_schedule(443 DispatchTime::At(when),444 maybe_periodic,445 priority,446 origin.caller().clone(),447 <ScheduledCall<T>>::new(*call)?,448 )?;449 Ok(())450 }451452 /// Cancel an anonymously scheduled task.453 #[pallet::weight(<T as Config>::WeightInfo::cancel(T::MaxScheduledPerBlock::get()))]454 pub fn cancel(origin: OriginFor<T>, when: T::BlockNumber, index: u32) -> DispatchResult {455 T::ScheduleOrigin::ensure_origin(origin.clone())?;456 let origin = <T as Config>::Origin::from(origin);457 Self::do_cancel(Some(origin.caller().clone()), (when, index))?;458 Ok(())459 }460461 /// Schedule a named task.462 #[pallet::weight(<T as Config>::WeightInfo::schedule_named(T::MaxScheduledPerBlock::get()))]463 pub fn schedule_named(464 origin: OriginFor<T>,465 id: TaskName,466 when: T::BlockNumber,467 maybe_periodic: Option<schedule::Period<T::BlockNumber>>,468 priority: schedule::Priority,469 call: Box<<T as Config>::Call>,470 ) -> DispatchResult {471 T::ScheduleOrigin::ensure_origin(origin.clone())?;472 let origin = <T as Config>::Origin::from(origin);473 Self::do_schedule_named(474 id,475 DispatchTime::At(when),476 maybe_periodic,477 priority,478 origin.caller().clone(),479 <ScheduledCall<T>>::new(*call)?,480 )?;481 Ok(())482 }483484 /// Cancel a named scheduled task.485 #[pallet::weight(<T as Config>::WeightInfo::cancel_named(T::MaxScheduledPerBlock::get()))]486 pub fn cancel_named(origin: OriginFor<T>, id: TaskName) -> DispatchResult {487 T::ScheduleOrigin::ensure_origin(origin.clone())?;488 let origin = <T as Config>::Origin::from(origin);489 Self::do_cancel_named(Some(origin.caller().clone()), id)?;490 Ok(())491 }492493 /// Anonymously schedule a task after a delay.494 ///495 /// # <weight>496 /// Same as [`schedule`].497 /// # </weight>498 #[pallet::weight(<T as Config>::WeightInfo::schedule(T::MaxScheduledPerBlock::get()))]499 pub fn schedule_after(500 origin: OriginFor<T>,501 after: T::BlockNumber,502 maybe_periodic: Option<schedule::Period<T::BlockNumber>>,503 priority: schedule::Priority,504 call: Box<<T as Config>::Call>,505 ) -> DispatchResult {506 T::ScheduleOrigin::ensure_origin(origin.clone())?;507 let origin = <T as Config>::Origin::from(origin);508 Self::do_schedule(509 DispatchTime::After(after),510 maybe_periodic,511 priority,512 origin.caller().clone(),513 <ScheduledCall<T>>::new(*call)?,514 )?;515 Ok(())516 }517518 /// Schedule a named task after a delay.519 ///520 /// # <weight>521 /// Same as [`schedule_named`](Self::schedule_named).522 /// # </weight>523 #[pallet::weight(<T as Config>::WeightInfo::schedule_named(T::MaxScheduledPerBlock::get()))]524 pub fn schedule_named_after(525 origin: OriginFor<T>,526 id: TaskName,527 after: T::BlockNumber,528 maybe_periodic: Option<schedule::Period<T::BlockNumber>>,529 priority: schedule::Priority,530 call: Box<<T as Config>::Call>,531 ) -> DispatchResult {532 T::ScheduleOrigin::ensure_origin(origin.clone())?;533 let origin = <T as Config>::Origin::from(origin);534 Self::do_schedule_named(535 id,536 DispatchTime::After(after),537 maybe_periodic,538 priority,539 origin.caller().clone(),540 <ScheduledCall<T>>::new(*call)?,541 )?;542 Ok(())543 }544 }545}546547impl<T: Config> Pallet<T> {548 fn resolve_time(when: DispatchTime<T::BlockNumber>) -> Result<T::BlockNumber, DispatchError> {549 let now = frame_system::Pallet::<T>::block_number();550551 let when = match when {552 DispatchTime::At(x) => x,553 // The current block has already completed it's scheduled tasks, so554 // Schedule the task at lest one block after this current block.555 DispatchTime::After(x) => now.saturating_add(x).saturating_add(One::one()),556 };557558 if when <= now {559 return Err(Error::<T>::TargetBlockNumberInPast.into());560 }561562 Ok(when)563 }564565 fn place_task(566 when: T::BlockNumber,567 what: ScheduledOf<T>,568 ) -> Result<TaskAddress<T::BlockNumber>, (DispatchError, ScheduledOf<T>)> {569 let maybe_name = what.maybe_id;570 let index = Self::push_to_agenda(when, what)?;571 let address = (when, index);572 if let Some(name) = maybe_name {573 Lookup::<T>::insert(name, address)574 }575 Self::deposit_event(Event::Scheduled {576 when: address.0,577 index: address.1,578 });579 Ok(address)580 }581582 fn push_to_agenda(583 when: T::BlockNumber,584 what: ScheduledOf<T>,585 ) -> Result<u32, (DispatchError, ScheduledOf<T>)> {586 let mut agenda = Agenda::<T>::get(when);587 let index = if (agenda.len() as u32) < T::MaxScheduledPerBlock::get() {588 // will always succeed due to the above check.589 let _ = agenda.try_push(Some(what));590 agenda.len() as u32 - 1591 } else {592 if let Some(hole_index) = agenda.iter().position(|i| i.is_none()) {593 agenda[hole_index] = Some(what);594 hole_index as u32595 } else {596 return Err((<Error<T>>::AgendaIsExhausted.into(), what));597 }598 };599 Agenda::<T>::insert(when, agenda);600 Ok(index)601 }602603 fn do_schedule(604 when: DispatchTime<T::BlockNumber>,605 maybe_periodic: Option<schedule::Period<T::BlockNumber>>,606 priority: schedule::Priority,607 origin: T::PalletsOrigin,608 call: ScheduledCall<T>,609 ) -> Result<TaskAddress<T::BlockNumber>, DispatchError> {610 let when = Self::resolve_time(when)?;611612 // sanitize maybe_periodic613 let maybe_periodic = maybe_periodic614 .filter(|p| p.1 > 1 && !p.0.is_zero())615 // Remove one from the number of repetitions since we will schedule one now.616 .map(|(p, c)| (p, c - 1));617 let task = Scheduled {618 maybe_id: None,619 priority,620 call,621 maybe_periodic,622 origin,623 _phantom: PhantomData,624 };625 Self::place_task(when, task).map_err(|x| x.0)626 }627628 fn do_cancel(629 origin: Option<T::PalletsOrigin>,630 (when, index): TaskAddress<T::BlockNumber>,631 ) -> Result<(), DispatchError> {632 let scheduled = Agenda::<T>::try_mutate(when, |agenda| {633 agenda.get_mut(index as usize).map_or(634 Ok(None),635 |s| -> Result<Option<Scheduled<_, _, _, _, _>>, DispatchError> {636 if let (Some(ref o), Some(ref s)) = (origin, s.borrow()) {637 if matches!(638 T::OriginPrivilegeCmp::cmp_privilege(o, &s.origin),639 Some(Ordering::Less) | None640 ) {641 return Err(BadOrigin.into());642 }643 };644 Ok(s.take())645 },646 )647 })?;648 if let Some(s) = scheduled {649 T::Preimages::drop(&s.call);650651 if let Some(id) = s.maybe_id {652 Lookup::<T>::remove(id);653 }654 Self::deposit_event(Event::Canceled { when, index });655 Ok(())656 } else {657 return Err(Error::<T>::NotFound.into());658 }659 }660661 fn do_schedule_named(662 id: TaskName,663 when: DispatchTime<T::BlockNumber>,664 maybe_periodic: Option<schedule::Period<T::BlockNumber>>,665 priority: schedule::Priority,666 origin: T::PalletsOrigin,667 call: ScheduledCall<T>,668 ) -> Result<TaskAddress<T::BlockNumber>, DispatchError> {669 // ensure id it is unique670 if Lookup::<T>::contains_key(&id) {671 return Err(Error::<T>::FailedToSchedule.into());672 }673674 let when = Self::resolve_time(when)?;675676 // sanitize maybe_periodic677 let maybe_periodic = maybe_periodic678 .filter(|p| p.1 > 1 && !p.0.is_zero())679 // Remove one from the number of repetitions since we will schedule one now.680 .map(|(p, c)| (p, c - 1));681682 let task = Scheduled {683 maybe_id: Some(id),684 priority,685 call,686 maybe_periodic,687 origin,688 _phantom: Default::default(),689 };690 Self::place_task(when, task).map_err(|x| x.0)691 }692693 fn do_cancel_named(origin: Option<T::PalletsOrigin>, id: TaskName) -> DispatchResult {694 Lookup::<T>::try_mutate_exists(id, |lookup| -> DispatchResult {695 if let Some((when, index)) = lookup.take() {696 let i = index as usize;697 Agenda::<T>::try_mutate(when, |agenda| -> DispatchResult {698 if let Some(s) = agenda.get_mut(i) {699 if let (Some(ref o), Some(ref s)) = (origin, s.borrow()) {700 if matches!(701 T::OriginPrivilegeCmp::cmp_privilege(o, &s.origin),702 Some(Ordering::Less) | None703 ) {704 return Err(BadOrigin.into());705 }706 T::Preimages::drop(&s.call);707 }708 *s = None;709 }710 Ok(())711 })?;712 Self::deposit_event(Event::Canceled { when, index });713 Ok(())714 } else {715 return Err(Error::<T>::NotFound.into());716 }717 })718 }719}720721enum ServiceTaskError {722 /// Could not be executed due to missing preimage.723 Unavailable,724 /// Could not be executed due to weight limitations.725 Overweight,726}727use ServiceTaskError::*;728729impl<T: Config> Pallet<T> {730 /// Service up to `max` agendas queue starting from earliest incompletely executed agenda.731 fn service_agendas(weight: &mut WeightCounter, now: T::BlockNumber, max: u32) {732 if !weight.check_accrue(T::WeightInfo::service_agendas_base()) {733 return;734 }735736 let mut incomplete_since = now + One::one();737 let mut when = IncompleteSince::<T>::take().unwrap_or(now);738 let mut executed = 0;739740 let max_items = T::MaxScheduledPerBlock::get();741 let mut count_down = max;742 let service_agenda_base_weight = T::WeightInfo::service_agenda_base(max_items);743 while count_down > 0 && when <= now && weight.can_accrue(service_agenda_base_weight) {744 if !Self::service_agenda(weight, &mut executed, now, when, u32::max_value()) {745 incomplete_since = incomplete_since.min(when);746 }747 when.saturating_inc();748 count_down.saturating_dec();749 }750 incomplete_since = incomplete_since.min(when);751 if incomplete_since <= now {752 IncompleteSince::<T>::put(incomplete_since);753 }754 }755756 /// Returns `true` if the agenda was fully completed, `false` if it should be revisited at a757 /// later block.758 fn service_agenda(759 weight: &mut WeightCounter,760 executed: &mut u32,761 now: T::BlockNumber,762 when: T::BlockNumber,763 max: u32,764 ) -> bool {765 let mut agenda = Agenda::<T>::get(when);766 let mut ordered = agenda767 .iter()768 .enumerate()769 .filter_map(|(index, maybe_item)| {770 maybe_item771 .as_ref()772 .map(|item| (index as u32, item.priority))773 })774 .collect::<Vec<_>>();775 ordered.sort_by_key(|k| k.1);776 let within_limit =777 weight.check_accrue(T::WeightInfo::service_agenda_base(ordered.len() as u32));778 debug_assert!(779 within_limit,780 "weight limit should have been checked in advance"781 );782783 // Items which we know can be executed and have postponed for execution in a later block.784 let mut postponed = (ordered.len() as u32).saturating_sub(max);785 // Items which we don't know can ever be executed.786 let mut dropped = 0;787788 for (agenda_index, _) in ordered.into_iter().take(max as usize) {789 let task = match agenda[agenda_index as usize].take() {790 None => continue,791 Some(t) => t,792 };793 let base_weight = T::WeightInfo::service_task(794 task.call.lookup_len().map(|x| x as usize),795 task.maybe_id.is_some(),796 task.maybe_periodic.is_some(),797 );798 if !weight.can_accrue(base_weight) {799 postponed += 1;800 break;801 }802 let result = Self::service_task(weight, now, when, agenda_index, *executed == 0, task);803 agenda[agenda_index as usize] = match result {804 Err((Unavailable, slot)) => {805 dropped += 1;806 slot807 }808 Err((Overweight, slot)) => {809 postponed += 1;810 slot811 }812 Ok(()) => {813 *executed += 1;814 None815 }816 };817 }818 if postponed > 0 || dropped > 0 {819 Agenda::<T>::insert(when, agenda);820 } else {821 Agenda::<T>::remove(when);822 }823 postponed == 0824 }825826 /// Service (i.e. execute) the given task, being careful not to overflow the `weight` counter.827 ///828 /// This involves:829 /// - removing and potentially replacing the `Lookup` entry for the task.830 /// - realizing the task's call which can include a preimage lookup.831 /// - Rescheduling the task for execution in a later agenda if periodic.832 fn service_task(833 weight: &mut WeightCounter,834 now: T::BlockNumber,835 when: T::BlockNumber,836 agenda_index: u32,837 is_first: bool,838 mut task: ScheduledOf<T>,839 ) -> Result<(), (ServiceTaskError, Option<ScheduledOf<T>>)> {840 if let Some(ref id) = task.maybe_id {841 Lookup::<T>::remove(id);842 }843844 let (call, lookup_len) = match T::Preimages::peek(&task.call) {845 Ok(c) => c,846 Err(_) => return Err((Unavailable, Some(task))),847 };848849 weight.check_accrue(T::WeightInfo::service_task(850 lookup_len.map(|x| x as usize),851 task.maybe_id.is_some(),852 task.maybe_periodic.is_some(),853 ));854855 match Self::execute_dispatch(weight, task.origin.clone(), call) {856 Err(Unavailable) => {857 debug_assert!(false, "Checked to exist with `peek`");858 Self::deposit_event(Event::CallUnavailable {859 task: (when, agenda_index),860 id: task.maybe_id,861 });862 Err((Unavailable, Some(task)))863 }864 Err(Overweight) if is_first => {865 T::Preimages::drop(&task.call);866 Self::deposit_event(Event::PermanentlyOverweight {867 task: (when, agenda_index),868 id: task.maybe_id,869 });870 Err((Unavailable, Some(task)))871 }872 Err(Overweight) => Err((Overweight, Some(task))),873 Ok(result) => {874 Self::deposit_event(Event::Dispatched {875 task: (when, agenda_index),876 id: task.maybe_id,877 result,878 });879 if let &Some((period, count)) = &task.maybe_periodic {880 if count > 1 {881 task.maybe_periodic = Some((period, count - 1));882 } else {883 task.maybe_periodic = None;884 }885 let wake = now.saturating_add(period);886 match Self::place_task(wake, task) {887 Ok(_) => {}888 Err((_, task)) => {889 // TODO: Leave task in storage somewhere for it to be rescheduled890 // manually.891 T::Preimages::drop(&task.call);892 Self::deposit_event(Event::PeriodicFailed {893 task: (when, agenda_index),894 id: task.maybe_id,895 });896 }897 }898 } else {899 T::Preimages::drop(&task.call);900 }901 Ok(())902 }903 }904 }905906 /// Make a dispatch to the given `call` from the given `origin`, ensuring that the `weight`907 /// counter does not exceed its limit and that it is counted accurately (e.g. accounted using908 /// post info if available).909 ///910 /// NOTE: Only the weight for this function will be counted (origin lookup, dispatch and the911 /// call itself).912 fn execute_dispatch(913 weight: &mut WeightCounter,914 origin: T::PalletsOrigin,915 call: <T as Config>::Call,916 ) -> Result<DispatchResult, ServiceTaskError> {917 let dispatch_origin: <T as Config>::Origin = origin.into();918 let base_weight = match dispatch_origin.clone().as_signed() {919 Some(_) => T::WeightInfo::execute_dispatch_signed(),920 _ => T::WeightInfo::execute_dispatch_unsigned(),921 };922 let call_weight = call.get_dispatch_info().weight;923 // We only allow a scheduled call if it cannot push the weight past the limit.924 let max_weight = base_weight.saturating_add(call_weight);925926 if !weight.can_accrue(max_weight) {927 return Err(Overweight);928 }929930 let (maybe_actual_call_weight, result) = match call.dispatch(dispatch_origin) {931 Ok(post_info) => (post_info.actual_weight, Ok(())),932 Err(error_and_info) => (933 error_and_info.post_info.actual_weight,934 Err(error_and_info.error),935 ),936 };937 let call_weight = maybe_actual_call_weight.unwrap_or(call_weight);938 weight.check_accrue(base_weight);939 weight.check_accrue(call_weight);940 Ok(result)941 }942}1// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.2// This file is part of Unique Network.34// Unique Network is free software: you can redistribute it and/or modify5// it under the terms of the GNU General Public License as published by6// the Free Software Foundation, either version 3 of the License, or7// (at your option) any later version.89// Unique Network is distributed in the hope that it will be useful,10// but WITHOUT ANY WARRANTY; without even the implied warranty of11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the12// GNU General Public License for more details.1314// You should have received a copy of the GNU General Public License15// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.1617// Original license:18// This file is part of Substrate.1920// Copyright (C) 2017-2022 Parity Technologies (UK) Ltd.21// SPDX-License-Identifier: Apache-2.02223// Licensed under the Apache License, Version 2.0 (the "License");24// you may not use this file except in compliance with the License.25// You may obtain a copy of the License at26//27// http://www.apache.org/licenses/LICENSE-2.028//29// Unless required by applicable law or agreed to in writing, software30// distributed under the License is distributed on an "AS IS" BASIS,31// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.32// See the License for the specific language governing permissions and33// limitations under the License.3435//! # Scheduler36//! A Pallet for scheduling dispatches.37//!38//! - [`Config`]39//! - [`Call`]40//! - [`Pallet`]41//!42//! ## Overview43//!44//! This Pallet exposes capabilities for scheduling dispatches to occur at a45//! specified block number or at a specified period. These scheduled dispatches46//! may be named or anonymous and may be canceled.47//!48//! **NOTE:** The scheduled calls will be dispatched with the default filter49//! for the origin: namely `frame_system::Config::BaseCallFilter` for all origin50//! except root which will get no filter. And not the filter contained in origin51//! use to call `fn schedule`.52//!53//! If a call is scheduled using proxy or whatever mecanism which adds filter,54//! then those filter will not be used when dispatching the schedule call.55//!56//! ## Interface57//!58//! ### Dispatchable Functions59//!60//! * `schedule` - schedule a dispatch, which may be periodic, to occur at a specified block and61//! with a specified priority.62//! * `cancel` - cancel a scheduled dispatch, specified by block number and index.63//! * `schedule_named` - augments the `schedule` interface with an additional `Vec<u8>` parameter64//! that can be used for identification.65//! * `cancel_named` - the named complement to the cancel function.6667// Ensure we're `no_std` when compiling for Wasm.68#![cfg_attr(not(feature = "std"), no_std)]6970#[cfg(feature = "runtime-benchmarks")]71mod benchmarking;72#[cfg(test)]73mod mock;74#[cfg(test)]75mod tests;76pub mod weights;7778use codec::{Codec, Decode, Encode, MaxEncodedLen};79use frame_support::{80 dispatch::{DispatchError, DispatchResult, Dispatchable, GetDispatchInfo, Parameter},81 traits::{82 schedule::{self, DispatchTime},83 EnsureOrigin, Get, IsType, OriginTrait, PrivilegeCmp, StorageVersion, PreimageRecipient,84 ConstU32, UnfilteredDispatchable,85 },86 weights::{Weight, PostDispatchInfo}, unsigned::TransactionValidityError,87};8889use frame_system::{self as system};90use scale_info::TypeInfo;91use sp_runtime::{92 traits::{BadOrigin, One, Saturating, Zero, Hash},93 BoundedVec, RuntimeDebug, DispatchErrorWithPostInfo,94};95use sp_core::H160;96use sp_std::{borrow::Borrow, cmp::Ordering, marker::PhantomData, prelude::*};97pub use weights::WeightInfo;9899pub use pallet::*;100101/// Just a simple index for naming period tasks.102pub type PeriodicIndex = u32;103/// The location of a scheduled task that can be used to remove it.104pub type TaskAddress<BlockNumber> = (BlockNumber, u32);105106pub type EncodedCall = BoundedVec<u8, ConstU32<128>>;107108#[derive(Clone, Eq, PartialEq, Encode, Decode, RuntimeDebug, TypeInfo, MaxEncodedLen)]109#[scale_info(skip_type_params(T))]110pub enum ScheduledCall<T: Config> {111 Inline(EncodedCall),112 PreimageLookup { hash: T::Hash, unbounded_len: u32 },113}114115impl<T: Config> ScheduledCall<T> {116 pub fn new(call: <T as Config>::Call) -> Result<Self, DispatchError> {117 let encoded = call.encode();118 let len = encoded.len();119120 match EncodedCall::try_from(encoded.clone()) {121 Ok(bounded) => Ok(Self::Inline(bounded)),122 Err(_) => {123 let hash = <T as system::Config>::Hashing::hash_of(&encoded);124 <T as Config>::Preimages::note_preimage(125 encoded126 .try_into()127 .map_err(|_| <Error<T>>::TooBigScheduledCall)?,128 );129130 Ok(Self::PreimageLookup {131 hash,132 unbounded_len: len as u32,133 })134 }135 }136 }137138 /// The maximum length of the lookup that is needed to peek `Self`.139 pub fn lookup_len(&self) -> Option<u32> {140 match self {141 Self::Inline(..) => None,142 Self::PreimageLookup { unbounded_len, .. } => Some(*unbounded_len),143 }144 }145146 /// Returns whether the image will require a lookup to be peeked.147 pub fn lookup_needed(&self) -> bool {148 match self {149 Self::Inline(_) => false,150 Self::PreimageLookup { .. } => true,151 }152 }153154 fn decode(mut data: &[u8]) -> Result<<T as Config>::Call, DispatchError> {155 <T as Config>::Call::decode(&mut data)156 .map_err(|_| <Error<T>>::ScheduledCallCorrupted.into())157 }158}159160pub trait SchedulerPreimages<T: Config>: PreimageRecipient<T::Hash> {161 fn drop(call: &ScheduledCall<T>);162163 fn peek(164 call: &ScheduledCall<T>,165 ) -> Result<(<T as pallet::Config>::Call, Option<u32>), DispatchError>;166167 /// Convert the given scheduled `call` value back into its original instance. If successful,168 /// `drop` any data backing it. This will not break the realisability of independently169 /// created instances of `ScheduledCall` which happen to have identical data.170 fn realize(171 call: &ScheduledCall<T>,172 ) -> Result<(<T as pallet::Config>::Call, Option<u32>), DispatchError>;173}174175impl<T: Config, PP: PreimageRecipient<T::Hash>> SchedulerPreimages<T> for PP {176 fn drop(call: &ScheduledCall<T>) {177 match call {178 ScheduledCall::Inline(_) => {}179 ScheduledCall::PreimageLookup { hash, .. } => Self::unrequest_preimage(hash),180 }181 }182183 fn peek(184 call: &ScheduledCall<T>,185 ) -> Result<(<T as pallet::Config>::Call, Option<u32>), DispatchError> {186 match call {187 ScheduledCall::Inline(data) => Ok((ScheduledCall::<T>::decode(data)?, None)),188 ScheduledCall::PreimageLookup {189 hash,190 unbounded_len,191 } => {192 let (preimage, len) = Self::get_preimage(hash)193 .ok_or(<Error<T>>::PreimageNotFound)194 .map(|preimage| (preimage, *unbounded_len))?;195196 Ok((ScheduledCall::<T>::decode(preimage.as_slice())?, Some(len)))197 }198 }199 }200201 fn realize(202 call: &ScheduledCall<T>,203 ) -> Result<(<T as pallet::Config>::Call, Option<u32>), DispatchError> {204 let r = Self::peek(call)?;205 Self::drop(call);206 Ok(r)207 }208}209210pub enum ScheduledEnsureOriginSuccess<AccountId> {211 Root,212 Signed(AccountId),213 Unsigned,214}215216pub type TaskName = [u8; 32];217218/// Information regarding an item to be executed in the future.219#[cfg_attr(any(feature = "std", test), derive(PartialEq, Eq))]220#[derive(Clone, RuntimeDebug, Encode, Decode, MaxEncodedLen, TypeInfo)]221pub struct Scheduled<Name, Call, BlockNumber, PalletsOrigin, AccountId> {222 /// The unique identity for this task, if there is one.223 maybe_id: Option<Name>,224225 /// This task's priority.226 priority: schedule::Priority,227228 /// The call to be dispatched.229 call: Call,230231 /// If the call is periodic, then this points to the information concerning that.232 maybe_periodic: Option<schedule::Period<BlockNumber>>,233234 /// The origin with which to dispatch the call.235 origin: PalletsOrigin,236 _phantom: PhantomData<AccountId>,237}238239pub type ScheduledOf<T> = Scheduled<240 TaskName,241 ScheduledCall<T>,242 <T as frame_system::Config>::BlockNumber,243 <T as Config>::PalletsOrigin,244 <T as frame_system::Config>::AccountId,245>;246247struct WeightCounter {248 used: Weight,249 limit: Weight,250}251252impl WeightCounter {253 fn check_accrue(&mut self, w: Weight) -> bool {254 let test = self.used.saturating_add(w);255 if test > self.limit {256 false257 } else {258 self.used = test;259 true260 }261 }262263 fn can_accrue(&mut self, w: Weight) -> bool {264 self.used.saturating_add(w) <= self.limit265 }266}267268pub(crate) trait MarginalWeightInfo: WeightInfo {269 fn service_task(maybe_lookup_len: Option<usize>, named: bool, periodic: bool) -> Weight {270 let base = Self::service_task_base();271 let mut total = match maybe_lookup_len {272 None => base,273 Some(l) => Self::service_task_fetched(l as u32),274 };275 if named {276 total.saturating_accrue(Self::service_task_named().saturating_sub(base));277 }278 if periodic {279 total.saturating_accrue(Self::service_task_periodic().saturating_sub(base));280 }281 total282 }283}284285impl<T: WeightInfo> MarginalWeightInfo for T {}286287#[frame_support::pallet]288pub mod pallet {289 use super::*;290 use frame_support::{dispatch::PostDispatchInfo, pallet_prelude::*};291 use system::pallet_prelude::*;292293 /// The current storage version.294 const STORAGE_VERSION: StorageVersion = StorageVersion::new(0);295296 #[pallet::pallet]297 #[pallet::generate_store(pub(super) trait Store)]298 #[pallet::storage_version(STORAGE_VERSION)]299 pub struct Pallet<T>(_);300301 #[pallet::config]302 pub trait Config: frame_system::Config {303 type Event: From<Event<Self>> + IsType<<Self as frame_system::Config>::Event>;304305 /// The aggregated origin which the dispatch will take.306 type Origin: OriginTrait<PalletsOrigin = Self::PalletsOrigin>307 + From<Self::PalletsOrigin>308 + IsType<<Self as system::Config>::Origin>309 + Clone;310311 /// The caller origin, overarching type of all pallets origins.312 type PalletsOrigin: From<system::RawOrigin<Self::AccountId>>313 + Codec314 + Clone315 + Eq316 + TypeInfo317 + MaxEncodedLen;318319 /// The aggregated call type.320 type Call: Parameter321 + Dispatchable<Origin = <Self as Config>::Origin, PostInfo = PostDispatchInfo>322 + UnfilteredDispatchable<Origin = <Self as system::Config>::Origin>323 + GetDispatchInfo324 + From<system::Call<Self>>;325326 /// The maximum weight that may be scheduled per block for any dispatchables.327 #[pallet::constant]328 type MaximumWeight: Get<Weight>;329330 /// Required origin to schedule or cancel calls.331 type ScheduleOrigin: EnsureOrigin<332 <Self as system::Config>::Origin,333 Success = ScheduledEnsureOriginSuccess<Self::AccountId>,334 >;335336 /// Compare the privileges of origins.337 ///338 /// This will be used when canceling a task, to ensure that the origin that tries339 /// to cancel has greater or equal privileges as the origin that created the scheduled task.340 ///341 /// For simplicity the [`EqualPrivilegeOnly`](frame_support::traits::EqualPrivilegeOnly) can342 /// be used. This will only check if two given origins are equal.343 type OriginPrivilegeCmp: PrivilegeCmp<Self::PalletsOrigin>;344345 /// The maximum number of scheduled calls in the queue for a single block.346 #[pallet::constant]347 type MaxScheduledPerBlock: Get<u32>;348349 /// Weight information for extrinsics in this pallet.350 type WeightInfo: WeightInfo;351352 /// The preimage provider with which we look up call hashes to get the call.353 type Preimages: SchedulerPreimages<Self>;354355 /// The helper type used for custom transaction fee logic.356 type CallExecutor: DispatchCall<Self, H160>;357 }358359 #[pallet::storage]360 pub type IncompleteSince<T: Config> = StorageValue<_, T::BlockNumber>;361362 /// Items to be executed, indexed by the block number that they should be executed on.363 #[pallet::storage]364 pub type Agenda<T: Config> = StorageMap<365 _,366 Twox64Concat,367 T::BlockNumber,368 BoundedVec<Option<ScheduledOf<T>>, T::MaxScheduledPerBlock>,369 ValueQuery,370 >;371372 /// Lookup from a name to the block number and index of the task.373 #[pallet::storage]374 pub(crate) type Lookup<T: Config> =375 StorageMap<_, Twox64Concat, TaskName, TaskAddress<T::BlockNumber>>;376377 /// Events type.378 #[pallet::event]379 #[pallet::generate_deposit(pub(super) fn deposit_event)]380 pub enum Event<T: Config> {381 /// Scheduled some task.382 Scheduled { when: T::BlockNumber, index: u32 },383 /// Canceled some task.384 Canceled { when: T::BlockNumber, index: u32 },385 /// Dispatched some task.386 Dispatched {387 task: TaskAddress<T::BlockNumber>,388 id: Option<[u8; 32]>,389 result: DispatchResult,390 },391 /// The call for the provided hash was not found so the task has been aborted.392 CallUnavailable {393 task: TaskAddress<T::BlockNumber>,394 id: Option<[u8; 32]>,395 },396 /// The given task was unable to be renewed since the agenda is full at that block.397 PeriodicFailed {398 task: TaskAddress<T::BlockNumber>,399 id: Option<[u8; 32]>,400 },401 /// The given task can never be executed since it is overweight.402 PermanentlyOverweight {403 task: TaskAddress<T::BlockNumber>,404 id: Option<[u8; 32]>,405 },406 }407408 #[pallet::error]409 pub enum Error<T> {410 /// Failed to schedule a call411 FailedToSchedule,412 /// There is no place for a new task in the agenda413 AgendaIsExhausted,414 /// Scheduled call is corrupted415 ScheduledCallCorrupted,416 /// Scheduled call preimage is not found417 PreimageNotFound,418 /// Scheduled call is too big419 TooBigScheduledCall,420 /// Cannot find the scheduled call.421 NotFound,422 /// Given target block number is in the past.423 TargetBlockNumberInPast,424 /// Reschedule failed because it does not change scheduled time.425 RescheduleNoChange,426 /// Attempt to use a non-named function on a named task.427 Named,428 }429430 #[pallet::hooks]431 impl<T: Config> Hooks<BlockNumberFor<T>> for Pallet<T> {432 /// Execute the scheduled calls433 fn on_initialize(now: T::BlockNumber) -> Weight {434 let mut weight_counter = WeightCounter {435 used: Weight::zero(),436 limit: T::MaximumWeight::get(),437 };438 Self::service_agendas(&mut weight_counter, now, u32::max_value());439 weight_counter.used440 }441 }442443 #[pallet::call]444 impl<T: Config> Pallet<T> {445 /// Anonymously schedule a task.446 #[pallet::weight(<T as Config>::WeightInfo::schedule(T::MaxScheduledPerBlock::get()))]447 pub fn schedule(448 origin: OriginFor<T>,449 when: T::BlockNumber,450 maybe_periodic: Option<schedule::Period<T::BlockNumber>>,451 priority: schedule::Priority,452 call: Box<<T as Config>::Call>,453 ) -> DispatchResult {454 T::ScheduleOrigin::ensure_origin(origin.clone())?;455 let origin = <T as Config>::Origin::from(origin);456 Self::do_schedule(457 DispatchTime::At(when),458 maybe_periodic,459 priority,460 origin.caller().clone(),461 <ScheduledCall<T>>::new(*call)?,462 )?;463 Ok(())464 }465466 /// Cancel an anonymously scheduled task.467 #[pallet::weight(<T as Config>::WeightInfo::cancel(T::MaxScheduledPerBlock::get()))]468 pub fn cancel(origin: OriginFor<T>, when: T::BlockNumber, index: u32) -> DispatchResult {469 T::ScheduleOrigin::ensure_origin(origin.clone())?;470 let origin = <T as Config>::Origin::from(origin);471 Self::do_cancel(Some(origin.caller().clone()), (when, index))?;472 Ok(())473 }474475 /// Schedule a named task.476 #[pallet::weight(<T as Config>::WeightInfo::schedule_named(T::MaxScheduledPerBlock::get()))]477 pub fn schedule_named(478 origin: OriginFor<T>,479 id: TaskName,480 when: T::BlockNumber,481 maybe_periodic: Option<schedule::Period<T::BlockNumber>>,482 priority: schedule::Priority,483 call: Box<<T as Config>::Call>,484 ) -> DispatchResult {485 T::ScheduleOrigin::ensure_origin(origin.clone())?;486 let origin = <T as Config>::Origin::from(origin);487 Self::do_schedule_named(488 id,489 DispatchTime::At(when),490 maybe_periodic,491 priority,492 origin.caller().clone(),493 <ScheduledCall<T>>::new(*call)?,494 )?;495 Ok(())496 }497498 /// Cancel a named scheduled task.499 #[pallet::weight(<T as Config>::WeightInfo::cancel_named(T::MaxScheduledPerBlock::get()))]500 pub fn cancel_named(origin: OriginFor<T>, id: TaskName) -> DispatchResult {501 T::ScheduleOrigin::ensure_origin(origin.clone())?;502 let origin = <T as Config>::Origin::from(origin);503 Self::do_cancel_named(Some(origin.caller().clone()), id)?;504 Ok(())505 }506507 /// Anonymously schedule a task after a delay.508 ///509 /// # <weight>510 /// Same as [`schedule`].511 /// # </weight>512 #[pallet::weight(<T as Config>::WeightInfo::schedule(T::MaxScheduledPerBlock::get()))]513 pub fn schedule_after(514 origin: OriginFor<T>,515 after: T::BlockNumber,516 maybe_periodic: Option<schedule::Period<T::BlockNumber>>,517 priority: schedule::Priority,518 call: Box<<T as Config>::Call>,519 ) -> DispatchResult {520 T::ScheduleOrigin::ensure_origin(origin.clone())?;521 let origin = <T as Config>::Origin::from(origin);522 Self::do_schedule(523 DispatchTime::After(after),524 maybe_periodic,525 priority,526 origin.caller().clone(),527 <ScheduledCall<T>>::new(*call)?,528 )?;529 Ok(())530 }531532 /// Schedule a named task after a delay.533 ///534 /// # <weight>535 /// Same as [`schedule_named`](Self::schedule_named).536 /// # </weight>537 #[pallet::weight(<T as Config>::WeightInfo::schedule_named(T::MaxScheduledPerBlock::get()))]538 pub fn schedule_named_after(539 origin: OriginFor<T>,540 id: TaskName,541 after: T::BlockNumber,542 maybe_periodic: Option<schedule::Period<T::BlockNumber>>,543 priority: schedule::Priority,544 call: Box<<T as Config>::Call>,545 ) -> DispatchResult {546 T::ScheduleOrigin::ensure_origin(origin.clone())?;547 let origin = <T as Config>::Origin::from(origin);548 Self::do_schedule_named(549 id,550 DispatchTime::After(after),551 maybe_periodic,552 priority,553 origin.caller().clone(),554 <ScheduledCall<T>>::new(*call)?,555 )?;556 Ok(())557 }558 }559}560561impl<T: Config> Pallet<T> {562 fn resolve_time(when: DispatchTime<T::BlockNumber>) -> Result<T::BlockNumber, DispatchError> {563 let now = frame_system::Pallet::<T>::block_number();564565 let when = match when {566 DispatchTime::At(x) => x,567 // The current block has already completed it's scheduled tasks, so568 // Schedule the task at lest one block after this current block.569 DispatchTime::After(x) => now.saturating_add(x).saturating_add(One::one()),570 };571572 if when <= now {573 return Err(Error::<T>::TargetBlockNumberInPast.into());574 }575576 Ok(when)577 }578579 fn place_task(580 when: T::BlockNumber,581 what: ScheduledOf<T>,582 ) -> Result<TaskAddress<T::BlockNumber>, (DispatchError, ScheduledOf<T>)> {583 let maybe_name = what.maybe_id;584 let index = Self::push_to_agenda(when, what)?;585 let address = (when, index);586 if let Some(name) = maybe_name {587 Lookup::<T>::insert(name, address)588 }589 Self::deposit_event(Event::Scheduled {590 when: address.0,591 index: address.1,592 });593 Ok(address)594 }595596 fn push_to_agenda(597 when: T::BlockNumber,598 what: ScheduledOf<T>,599 ) -> Result<u32, (DispatchError, ScheduledOf<T>)> {600 let mut agenda = Agenda::<T>::get(when);601 let index = if (agenda.len() as u32) < T::MaxScheduledPerBlock::get() {602 // will always succeed due to the above check.603 let _ = agenda.try_push(Some(what));604 agenda.len() as u32 - 1605 } else {606 if let Some(hole_index) = agenda.iter().position(|i| i.is_none()) {607 agenda[hole_index] = Some(what);608 hole_index as u32609 } else {610 return Err((<Error<T>>::AgendaIsExhausted.into(), what));611 }612 };613 Agenda::<T>::insert(when, agenda);614 Ok(index)615 }616617 fn do_schedule(618 when: DispatchTime<T::BlockNumber>,619 maybe_periodic: Option<schedule::Period<T::BlockNumber>>,620 priority: schedule::Priority,621 origin: T::PalletsOrigin,622 call: ScheduledCall<T>,623 ) -> Result<TaskAddress<T::BlockNumber>, DispatchError> {624 let when = Self::resolve_time(when)?;625626 // sanitize maybe_periodic627 let maybe_periodic = maybe_periodic628 .filter(|p| p.1 > 1 && !p.0.is_zero())629 // Remove one from the number of repetitions since we will schedule one now.630 .map(|(p, c)| (p, c - 1));631 let task = Scheduled {632 maybe_id: None,633 priority,634 call,635 maybe_periodic,636 origin,637 _phantom: PhantomData,638 };639 Self::place_task(when, task).map_err(|x| x.0)640 }641642 fn do_cancel(643 origin: Option<T::PalletsOrigin>,644 (when, index): TaskAddress<T::BlockNumber>,645 ) -> Result<(), DispatchError> {646 let scheduled = Agenda::<T>::try_mutate(when, |agenda| {647 agenda.get_mut(index as usize).map_or(648 Ok(None),649 |s| -> Result<Option<Scheduled<_, _, _, _, _>>, DispatchError> {650 if let (Some(ref o), Some(ref s)) = (origin, s.borrow()) {651 if matches!(652 T::OriginPrivilegeCmp::cmp_privilege(o, &s.origin),653 Some(Ordering::Less) | None654 ) {655 return Err(BadOrigin.into());656 }657 };658 Ok(s.take())659 },660 )661 })?;662 if let Some(s) = scheduled {663 T::Preimages::drop(&s.call);664665 if let Some(id) = s.maybe_id {666 Lookup::<T>::remove(id);667 }668 Self::deposit_event(Event::Canceled { when, index });669 Ok(())670 } else {671 return Err(Error::<T>::NotFound.into());672 }673 }674675 fn do_schedule_named(676 id: TaskName,677 when: DispatchTime<T::BlockNumber>,678 maybe_periodic: Option<schedule::Period<T::BlockNumber>>,679 priority: schedule::Priority,680 origin: T::PalletsOrigin,681 call: ScheduledCall<T>,682 ) -> Result<TaskAddress<T::BlockNumber>, DispatchError> {683 // ensure id it is unique684 if Lookup::<T>::contains_key(&id) {685 return Err(Error::<T>::FailedToSchedule.into());686 }687688 let when = Self::resolve_time(when)?;689690 // sanitize maybe_periodic691 let maybe_periodic = maybe_periodic692 .filter(|p| p.1 > 1 && !p.0.is_zero())693 // Remove one from the number of repetitions since we will schedule one now.694 .map(|(p, c)| (p, c - 1));695696 let task = Scheduled {697 maybe_id: Some(id),698 priority,699 call,700 maybe_periodic,701 origin,702 _phantom: Default::default(),703 };704 Self::place_task(when, task).map_err(|x| x.0)705 }706707 fn do_cancel_named(origin: Option<T::PalletsOrigin>, id: TaskName) -> DispatchResult {708 Lookup::<T>::try_mutate_exists(id, |lookup| -> DispatchResult {709 if let Some((when, index)) = lookup.take() {710 let i = index as usize;711 Agenda::<T>::try_mutate(when, |agenda| -> DispatchResult {712 if let Some(s) = agenda.get_mut(i) {713 if let (Some(ref o), Some(ref s)) = (origin, s.borrow()) {714 if matches!(715 T::OriginPrivilegeCmp::cmp_privilege(o, &s.origin),716 Some(Ordering::Less) | None717 ) {718 return Err(BadOrigin.into());719 }720 T::Preimages::drop(&s.call);721 }722 *s = None;723 }724 Ok(())725 })?;726 Self::deposit_event(Event::Canceled { when, index });727 Ok(())728 } else {729 return Err(Error::<T>::NotFound.into());730 }731 })732 }733}734735enum ServiceTaskError {736 /// Could not be executed due to missing preimage.737 Unavailable,738 /// Could not be executed due to weight limitations.739 Overweight,740}741use ServiceTaskError::*;742743/// A Scheduler-Runtime interface for finer payment handling.744pub trait DispatchCall<T: frame_system::Config + Config, SelfContainedSignedInfo> {745 /// Resolve the call dispatch, including any post-dispatch operations.746 fn dispatch_call(747 signer: Option<T::AccountId>,748 function: <T as Config>::Call,749 ) -> Result<750 Result<PostDispatchInfo, DispatchErrorWithPostInfo<PostDispatchInfo>>,751 TransactionValidityError,752 >;753}754755impl<T: Config> Pallet<T> {756 /// Service up to `max` agendas queue starting from earliest incompletely executed agenda.757 fn service_agendas(weight: &mut WeightCounter, now: T::BlockNumber, max: u32) {758 if !weight.check_accrue(T::WeightInfo::service_agendas_base()) {759 return;760 }761762 let mut incomplete_since = now + One::one();763 let mut when = IncompleteSince::<T>::take().unwrap_or(now);764 let mut executed = 0;765766 let max_items = T::MaxScheduledPerBlock::get();767 let mut count_down = max;768 let service_agenda_base_weight = T::WeightInfo::service_agenda_base(max_items);769 while count_down > 0 && when <= now && weight.can_accrue(service_agenda_base_weight) {770 if !Self::service_agenda(weight, &mut executed, now, when, u32::max_value()) {771 incomplete_since = incomplete_since.min(when);772 }773 when.saturating_inc();774 count_down.saturating_dec();775 }776 incomplete_since = incomplete_since.min(when);777 if incomplete_since <= now {778 IncompleteSince::<T>::put(incomplete_since);779 }780 }781782 /// Returns `true` if the agenda was fully completed, `false` if it should be revisited at a783 /// later block.784 fn service_agenda(785 weight: &mut WeightCounter,786 executed: &mut u32,787 now: T::BlockNumber,788 when: T::BlockNumber,789 max: u32,790 ) -> bool {791 let mut agenda = Agenda::<T>::get(when);792 let mut ordered = agenda793 .iter()794 .enumerate()795 .filter_map(|(index, maybe_item)| {796 maybe_item797 .as_ref()798 .map(|item| (index as u32, item.priority))799 })800 .collect::<Vec<_>>();801 ordered.sort_by_key(|k| k.1);802 let within_limit =803 weight.check_accrue(T::WeightInfo::service_agenda_base(ordered.len() as u32));804 debug_assert!(805 within_limit,806 "weight limit should have been checked in advance"807 );808809 // Items which we know can be executed and have postponed for execution in a later block.810 let mut postponed = (ordered.len() as u32).saturating_sub(max);811 // Items which we don't know can ever be executed.812 let mut dropped = 0;813814 for (agenda_index, _) in ordered.into_iter().take(max as usize) {815 let task = match agenda[agenda_index as usize].take() {816 None => continue,817 Some(t) => t,818 };819 let base_weight = T::WeightInfo::service_task(820 task.call.lookup_len().map(|x| x as usize),821 task.maybe_id.is_some(),822 task.maybe_periodic.is_some(),823 );824 if !weight.can_accrue(base_weight) {825 postponed += 1;826 break;827 }828 let result = Self::service_task(weight, now, when, agenda_index, *executed == 0, task);829 agenda[agenda_index as usize] = match result {830 Err((Unavailable, slot)) => {831 dropped += 1;832 slot833 }834 Err((Overweight, slot)) => {835 postponed += 1;836 slot837 }838 Ok(()) => {839 *executed += 1;840 None841 }842 };843 }844 if postponed > 0 || dropped > 0 {845 Agenda::<T>::insert(when, agenda);846 } else {847 Agenda::<T>::remove(when);848 }849 postponed == 0850 }851852 /// Service (i.e. execute) the given task, being careful not to overflow the `weight` counter.853 ///854 /// This involves:855 /// - removing and potentially replacing the `Lookup` entry for the task.856 /// - realizing the task's call which can include a preimage lookup.857 /// - Rescheduling the task for execution in a later agenda if periodic.858 fn service_task(859 weight: &mut WeightCounter,860 now: T::BlockNumber,861 when: T::BlockNumber,862 agenda_index: u32,863 is_first: bool,864 mut task: ScheduledOf<T>,865 ) -> Result<(), (ServiceTaskError, Option<ScheduledOf<T>>)> {866 if let Some(ref id) = task.maybe_id {867 Lookup::<T>::remove(id);868 }869870 let (call, lookup_len) = match T::Preimages::peek(&task.call) {871 Ok(c) => c,872 Err(_) => return Err((Unavailable, Some(task))),873 };874875 weight.check_accrue(T::WeightInfo::service_task(876 lookup_len.map(|x| x as usize),877 task.maybe_id.is_some(),878 task.maybe_periodic.is_some(),879 ));880881 match Self::execute_dispatch(weight, task.origin.clone(), call) {882 Err(Unavailable) => {883 debug_assert!(false, "Checked to exist with `peek`");884 Self::deposit_event(Event::CallUnavailable {885 task: (when, agenda_index),886 id: task.maybe_id,887 });888 Err((Unavailable, Some(task)))889 }890 Err(Overweight) if is_first => {891 T::Preimages::drop(&task.call);892 Self::deposit_event(Event::PermanentlyOverweight {893 task: (when, agenda_index),894 id: task.maybe_id,895 });896 Err((Unavailable, Some(task)))897 }898 Err(Overweight) => Err((Overweight, Some(task))),899 Ok(result) => {900 Self::deposit_event(Event::Dispatched {901 task: (when, agenda_index),902 id: task.maybe_id,903 result,904 });905 if let &Some((period, count)) = &task.maybe_periodic {906 if count > 1 {907 task.maybe_periodic = Some((period, count - 1));908 } else {909 task.maybe_periodic = None;910 }911 let wake = now.saturating_add(period);912 match Self::place_task(wake, task) {913 Ok(_) => {}914 Err((_, task)) => {915 // TODO: Leave task in storage somewhere for it to be rescheduled916 // manually.917 T::Preimages::drop(&task.call);918 Self::deposit_event(Event::PeriodicFailed {919 task: (when, agenda_index),920 id: task.maybe_id,921 });922 }923 }924 } else {925 T::Preimages::drop(&task.call);926 }927 Ok(())928 }929 }930 }931932 /// Make a dispatch to the given `call` from the given `origin`, ensuring that the `weight`933 /// counter does not exceed its limit and that it is counted accurately (e.g. accounted using934 /// post info if available).935 ///936 /// NOTE: Only the weight for this function will be counted (origin lookup, dispatch and the937 /// call itself).938 fn execute_dispatch(939 weight: &mut WeightCounter,940 origin: T::PalletsOrigin,941 call: <T as Config>::Call,942 ) -> Result<DispatchResult, ServiceTaskError> {943 let dispatch_origin: <T as Config>::Origin = origin.into();944 let base_weight = match dispatch_origin.clone().as_signed() {945 Some(_) => T::WeightInfo::execute_dispatch_signed(),946 _ => T::WeightInfo::execute_dispatch_unsigned(),947 };948 let call_weight = call.get_dispatch_info().weight;949 // We only allow a scheduled call if it cannot push the weight past the limit.950 let max_weight = base_weight.saturating_add(call_weight);951952 if !weight.can_accrue(max_weight) {953 return Err(Overweight);954 }955956 // let scheduled_origin =957 // <<T as Config>::Origin as From<T::PalletsOrigin>>::from(origin.clone());958 let ensured_origin = T::ScheduleOrigin::ensure_origin(dispatch_origin.into());959960 let r = match ensured_origin {961 Ok(ScheduledEnsureOriginSuccess::Root) => {962 Ok(call.dispatch_bypass_filter(frame_system::RawOrigin::Root.into()))963 },964 Ok(ScheduledEnsureOriginSuccess::Signed(sender)) => {965 // Execute transaction via chain default pipeline966 // That means dispatch will be processed like any user's extrinsic e.g. transaction fees will be taken967 T::CallExecutor::dispatch_call(Some(sender), call.clone())968 },969 Ok(ScheduledEnsureOriginSuccess::Unsigned) => {970 // Unsigned version of the above971 T::CallExecutor::dispatch_call(None, call.clone())972 }973 Err(e) => Ok(Err(e.into())),974 };975976 let (maybe_actual_call_weight, result) = match r {977 Ok(result) => match result {978 Ok(post_info) => (post_info.actual_weight, Ok(())),979 Err(error_and_info) => (980 error_and_info.post_info.actual_weight,981 Err(error_and_info.error),982 ),983 },984 Err(_) => {985 log::error!(986 target: "runtime::scheduler",987 "Warning: Scheduler has failed to execute a post-dispatch transaction. \988 This block might have become invalid.");989 (None, Err(DispatchError::CannotLookup))990 }991 };992 let call_weight = maybe_actual_call_weight.unwrap_or(call_weight);993 weight.check_accrue(base_weight);994 weight.check_accrue(call_weight);995 Ok(result)996 }997}runtime/common/config/pallets/scheduler.rsdiffbeforeafterboth--- a/runtime/common/config/pallets/scheduler.rs
+++ b/runtime/common/config/pallets/scheduler.rs
@@ -27,7 +27,7 @@
runtime_common::{scheduler::SchedulerPaymentExecutor, config::substrate::RuntimeBlockWeights},
Runtime, RuntimeCall, RuntimeEvent, RuntimeOrigin, OriginCaller, Balances,
};
-use pallet_unique_scheduler::ScheduledEnsureOriginSuccess;
+use pallet_unique_scheduler_v2::ScheduledEnsureOriginSuccess;
use up_common::types::AccountId;
parameter_types! {
@@ -98,4 +98,5 @@
type MaxScheduledPerBlock = MaxScheduledPerBlock;
type WeightInfo = ();
type Preimages = ();
+ type CallExecutor = SchedulerPaymentExecutor;
}
runtime/common/scheduler.rsdiffbeforeafterboth--- a/runtime/common/scheduler.rs
+++ b/runtime/common/scheduler.rs
@@ -28,11 +28,11 @@
use crate::{Runtime, RuntimeCall, RuntimeOrigin, Balances, maintenance};
use up_common::types::{AccountId, Balance};
use fp_self_contained::SelfContainedCall;
-use pallet_unique_scheduler::DispatchCall;
+use pallet_unique_scheduler_v2::DispatchCall;
use pallet_transaction_payment::ChargeTransactionPayment;
-type SponsorshipChargeTransactionPayment =
- pallet_charge_transaction::ChargeTransactionPayment<Runtime>;
+// type SponsorshipChargeTransactionPayment =
+// pallet_charge_transaction::ChargeTransactionPayment<Runtime>;
/// The SignedExtension to the basic transaction logic.
pub type SignedExtraScheduler = (
@@ -61,7 +61,7 @@
pub struct SchedulerPaymentExecutor;
-impl<T: frame_system::Config + pallet_unique_scheduler::Config, SelfContainedSignedInfo>
+impl<T: frame_system::Config + pallet_unique_scheduler_v2::Config, SelfContainedSignedInfo>
DispatchCall<T, SelfContainedSignedInfo> for SchedulerPaymentExecutor
where
<T as frame_system::Config>::RuntimeCall: Member
@@ -71,13 +71,13 @@
+ From<frame_system::Call<Runtime>>,
SelfContainedSignedInfo: Send + Sync + 'static,
RuntimeCall: From<<T as frame_system::Config>::RuntimeCall>
- + From<<T as pallet_unique_scheduler::Config>::RuntimeCall>
+ + From<<T as pallet_unique_scheduler_v2::Config>::RuntimeCall>
+ SelfContainedCall<SignedInfo = SelfContainedSignedInfo>,
sp_runtime::AccountId32: From<<T as frame_system::Config>::AccountId>,
{
fn dispatch_call(
signer: Option<<T as frame_system::Config>::AccountId>,
- call: <T as pallet_unique_scheduler::Config>::RuntimeCall,
+ call: <T as pallet_unique_scheduler_v2::Config>::RuntimeCall,
) -> Result<
Result<PostDispatchInfo, DispatchErrorWithPostInfo<PostDispatchInfo>>,
TransactionValidityError,
@@ -105,52 +105,99 @@
extrinsic.apply::<Runtime>(&dispatch_info, len)
}
+}
- fn reserve_balance(
- id: [u8; 16],
- sponsor: <T as frame_system::Config>::AccountId,
- call: <T as pallet_unique_scheduler::Config>::RuntimeCall,
- count: u32,
- ) -> Result<(), DispatchError> {
- let dispatch_info = call.get_dispatch_info();
- let weight: Balance =
- SponsorshipChargeTransactionPayment::traditional_fee(0, &dispatch_info, 0)
- .saturating_mul(count.into());
- <Balances as NamedReservableCurrency<AccountId>>::reserve_named(
- &id,
- &(sponsor.into()),
- weight,
- )
- }
+// impl<T: frame_system::Config + pallet_unique_scheduler::Config, SelfContainedSignedInfo>
+// DispatchCall<T, SelfContainedSignedInfo> for SchedulerPaymentExecutor
+// where
+// <T as frame_system::Config>::Call: Member
+// + Dispatchable<Origin = Origin, Info = DispatchInfo>
+// + SelfContainedCall<SignedInfo = SelfContainedSignedInfo>
+// + GetDispatchInfo
+// + From<frame_system::Call<Runtime>>,
+// SelfContainedSignedInfo: Send + Sync + 'static,
+// Call: From<<T as frame_system::Config>::Call>
+// + From<<T as pallet_unique_scheduler::Config>::Call>
+// + SelfContainedCall<SignedInfo = SelfContainedSignedInfo>,
+// sp_runtime::AccountId32: From<<T as frame_system::Config>::AccountId>,
+// {
+// fn dispatch_call(
+// signer: Option<<T as frame_system::Config>::AccountId>,
+// call: <T as pallet_unique_scheduler::Config>::Call,
+// ) -> Result<
+// Result<PostDispatchInfo, DispatchErrorWithPostInfo<PostDispatchInfo>>,
+// TransactionValidityError,
+// > {
+// let dispatch_info = call.get_dispatch_info();
+// let len = call.encoded_size();
- fn pay_for_call(
- id: [u8; 16],
- sponsor: <T as frame_system::Config>::AccountId,
- call: <T as pallet_unique_scheduler::Config>::RuntimeCall,
- ) -> Result<u128, DispatchError> {
- let dispatch_info = call.get_dispatch_info();
- let weight: Balance =
- SponsorshipChargeTransactionPayment::traditional_fee(0, &dispatch_info, 0);
- Ok(
- <Balances as NamedReservableCurrency<AccountId>>::unreserve_named(
- &id,
- &(sponsor.into()),
- weight,
- ),
- )
- }
+// let signed = match signer {
+// Some(signer) => fp_self_contained::CheckedSignature::Signed(
+// signer.clone().into(),
+// get_signed_extras(signer.into()),
+// ),
+// None => fp_self_contained::CheckedSignature::Unsigned,
+// };
- fn cancel_reserve(
- id: [u8; 16],
- sponsor: <T as frame_system::Config>::AccountId,
- ) -> Result<u128, DispatchError> {
- Ok(
- <Balances as NamedReservableCurrency<AccountId>>::unreserve_named(
- &id,
- &(sponsor.into()),
- u128::MAX,
- ),
- )
- }
-}
+// let extrinsic = fp_self_contained::CheckedExtrinsic::<
+// AccountId,
+// Call,
+// SignedExtraScheduler,
+// SelfContainedSignedInfo,
+// > {
+// signed,
+// function: call.into(),
+// };
+
+// extrinsic.apply::<Runtime>(&dispatch_info, len)
+// }
+
+// fn reserve_balance(
+// id: [u8; 16],
+// sponsor: <T as frame_system::Config>::AccountId,
+// call: <T as pallet_unique_scheduler::Config>::Call,
+// count: u32,
+// ) -> Result<(), DispatchError> {
+// let dispatch_info = call.get_dispatch_info();
+// let weight: Balance =
+// SponsorshipChargeTransactionPayment::traditional_fee(0, &dispatch_info, 0)
+// .saturating_mul(count.into());
+
+// <Balances as NamedReservableCurrency<AccountId>>::reserve_named(
+// &id,
+// &(sponsor.into()),
+// weight,
+// )
+// }
+
+// fn pay_for_call(
+// id: [u8; 16],
+// sponsor: <T as frame_system::Config>::AccountId,
+// call: <T as pallet_unique_scheduler::Config>::Call,
+// ) -> Result<u128, DispatchError> {
+// let dispatch_info = call.get_dispatch_info();
+// let weight: Balance =
+// SponsorshipChargeTransactionPayment::traditional_fee(0, &dispatch_info, 0);
+// Ok(
+// <Balances as NamedReservableCurrency<AccountId>>::unreserve_named(
+// &id,
+// &(sponsor.into()),
+// weight,
+// ),
+// )
+// }
+
+// fn cancel_reserve(
+// id: [u8; 16],
+// sponsor: <T as frame_system::Config>::AccountId,
+// ) -> Result<u128, DispatchError> {
+// Ok(
+// <Balances as NamedReservableCurrency<AccountId>>::unreserve_named(
+// &id,
+// &(sponsor.into()),
+// u128::MAX,
+// ),
+// )
+// }
+// }