git.delta.rocks / unique-network / refs/commits / bf9570200bab

difftreelog

source

pallets/scheduler/src/lib.rs28.1 KiBsourcehistory
1// This file is part of Substrate.23// Copyright (C) 2017-2022 Parity Technologies (UK) Ltd.4// SPDX-License-Identifier: Apache-2.056// 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 at9//10// 	http://www.apache.org/licenses/LICENSE-2.011//12// Unless required by applicable law or agreed to in writing, software13// 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 and16// limitations under the License.1718//! # Scheduler19//! A Pallet for scheduling dispatches.20//!21//! - [`Config`]22//! - [`Call`]23//! - [`Pallet`]24//!25//! ## Overview26//!27//! This Pallet exposes capabilities for scheduling dispatches to occur at a28//! specified block number or at a specified period. These scheduled dispatches29//! may be named or anonymous and may be canceled.30//!31//! **NOTE:** The scheduled calls will be dispatched with the default filter32//! for the origin: namely `frame_system::Config::BaseCallFilter` for all origin33//! except root which will get no filter. And not the filter contained in origin34//! use to call `fn schedule`.35//!36//! If a call is scheduled using proxy or whatever mecanism which adds filter,37//! then those filter will not be used when dispatching the schedule call.38//!39//! ## Interface40//!41//! ### Dispatchable Functions42//!43//! * `schedule` - schedule a dispatch, which may be periodic, to occur at a specified block and44//!   with a specified priority.45//! * `cancel` - cancel a scheduled dispatch, specified by block number and index.46//! * `schedule_named` - augments the `schedule` interface with an additional `Vec<u8>` parameter47//!   that can be used for identification.48//! * `cancel_named` - the named complement to the cancel function.4950// Ensure we're `no_std` when compiling for Wasm.51#![cfg_attr(not(feature = "std"), no_std)]5253#[cfg(feature = "runtime-benchmarks")]54mod benchmarking;5556pub mod weights;5758use codec::{Codec, Decode, Encode};59use frame_support::{60	dispatch::{DispatchError, DispatchResult, Dispatchable, Parameter},61	traits::{62		schedule::{self, DispatchTime, MaybeHashed},63		NamedReservableCurrency, EnsureOrigin, Get, IsType, OriginTrait, PrivilegeCmp,64		StorageVersion,65	},66	weights::{GetDispatchInfo, Weight},67};68use frame_system::{self as system, ensure_signed};69pub use pallet::*;70use scale_info::TypeInfo;71use sp_runtime::{72	traits::{BadOrigin, One, Saturating, Zero},73	RuntimeDebug, DispatchErrorWithPostInfo,74};75use sp_std::{borrow::Borrow, cmp::Ordering, marker::PhantomData, prelude::*};76use sp_core::H160;77pub use weights::WeightInfo;7879/// Just a simple index for naming period tasks.80pub type PeriodicIndex = u32;81/// The location of a scheduled task that can be used to remove it.82pub type TaskAddress<BlockNumber> = (BlockNumber, u32);83pub const MAX_TASK_ID_LENGTH_IN_BYTES: u8 = 16;8485type ScheduledId = [u8; MAX_TASK_ID_LENGTH_IN_BYTES as usize];86pub type CallOrHashOf<T> = MaybeHashed<<T as Config>::Call, <T as frame_system::Config>::Hash>;8788/// Information regarding an item to be executed in the future.89#[cfg_attr(any(feature = "std", test), derive(PartialEq, Eq))]90#[derive(Clone, RuntimeDebug, Encode, Decode, TypeInfo)]91pub struct ScheduledV3<Call, BlockNumber, PalletsOrigin, AccountId> {92	/// The unique identity for this task, if there is one.93	maybe_id: Option<ScheduledId>,94	/// This task's priority.95	priority: schedule::Priority,96	/// The call to be dispatched.97	call: Call,98	/// If the call is periodic, then this points to the information concerning that.99	maybe_periodic: Option<schedule::Period<BlockNumber>>,100	/// The origin to dispatch the call.101	origin: PalletsOrigin,102	_phantom: PhantomData<AccountId>,103}104105pub type ScheduledV3Of<T> = ScheduledV3<106	CallOrHashOf<T>,107	<T as frame_system::Config>::BlockNumber,108	<T as Config>::PalletsOrigin,109	<T as frame_system::Config>::AccountId,110>;111112pub type ScheduledOf<T> = ScheduledV3Of<T>;113114/// The current version of Scheduled struct.115pub type Scheduled<Call, BlockNumber, PalletsOrigin, AccountId> =116	ScheduledV3<Call, BlockNumber, PalletsOrigin, AccountId>;117118#[cfg(feature = "runtime-benchmarks")]119mod preimage_provider {120	use frame_support::traits::PreimageRecipient;121	pub trait PreimageProviderAndMaybeRecipient<H>: PreimageRecipient<H> {}122	impl<H, T: PreimageRecipient<H>> PreimageProviderAndMaybeRecipient<H> for T {}123}124125#[cfg(not(feature = "runtime-benchmarks"))]126mod preimage_provider {127	use frame_support::traits::PreimageProvider;128	pub trait PreimageProviderAndMaybeRecipient<H>: PreimageProvider<H> {}129	impl<H, T: PreimageProvider<H>> PreimageProviderAndMaybeRecipient<H> for T {}130}131132pub use preimage_provider::PreimageProviderAndMaybeRecipient;133134pub(crate) trait MarginalWeightInfo: WeightInfo {135	fn item(periodic: bool, named: bool, resolved: Option<bool>) -> Weight {136		match (periodic, named, resolved) {137			(_, false, None) => Self::on_initialize_aborted(2) - Self::on_initialize_aborted(1),138			(_, true, None) => {139				Self::on_initialize_named_aborted(2) - Self::on_initialize_named_aborted(1)140			}141			(false, false, Some(false)) => Self::on_initialize(2) - Self::on_initialize(1),142			(false, true, Some(false)) => {143				Self::on_initialize_named(2) - Self::on_initialize_named(1)144			}145			(true, false, Some(false)) => {146				Self::on_initialize_periodic(2) - Self::on_initialize_periodic(1)147			}148			(true, true, Some(false)) => {149				Self::on_initialize_periodic_named(2) - Self::on_initialize_periodic_named(1)150			}151			(false, false, Some(true)) => {152				Self::on_initialize_resolved(2) - Self::on_initialize_resolved(1)153			}154			(false, true, Some(true)) => {155				Self::on_initialize_named_resolved(2) - Self::on_initialize_named_resolved(1)156			}157			(true, false, Some(true)) => {158				Self::on_initialize_periodic_resolved(2) - Self::on_initialize_periodic_resolved(1)159			}160			(true, true, Some(true)) => {161				Self::on_initialize_periodic_named_resolved(2)162					- Self::on_initialize_periodic_named_resolved(1)163			}164		}165	}166}167impl<T: WeightInfo> MarginalWeightInfo for T {}168169#[frame_support::pallet]170pub mod pallet {171	use super::*;172	use frame_support::{173		dispatch::PostDispatchInfo,174		pallet_prelude::*,175		traits::{schedule::LookupError, PreimageProvider},176	};177	use frame_system::pallet_prelude::*;178179	/// The current storage version.180	const STORAGE_VERSION: StorageVersion = StorageVersion::new(3);181182	#[pallet::pallet]183	#[pallet::generate_store(pub(super) trait Store)]184	#[pallet::storage_version(STORAGE_VERSION)]185	#[pallet::without_storage_info]186	pub struct Pallet<T>(_);187188	/// `system::Config` should always be included in our implied traits.189	#[pallet::config]190	pub trait Config: frame_system::Config {191		/// The overarching event type.192		type Event: From<Event<Self>> + IsType<<Self as frame_system::Config>::Event>;193194		/// The aggregated origin which the dispatch will take.195		type Origin: OriginTrait<PalletsOrigin = Self::PalletsOrigin>196			+ From<Self::PalletsOrigin>197			+ IsType<<Self as system::Config>::Origin>;198199		/// The caller origin, overarching type of all pallets origins.200		type PalletsOrigin: From<system::RawOrigin<Self::AccountId>> + Codec + Clone + Eq + TypeInfo;201202		type Currency: NamedReservableCurrency<Self::AccountId, ReserveIdentifier = ScheduledId>;203204		/// The aggregated call type.205		type Call: Parameter206			+ Dispatchable<Origin = <Self as Config>::Origin, PostInfo = PostDispatchInfo>207			+ GetDispatchInfo208			+ From<system::Call<Self>>;209210		/// The maximum weight that may be scheduled per block for any dispatchables of less211		/// priority than `schedule::HARD_DEADLINE`.212		#[pallet::constant]213		type MaximumWeight: Get<Weight>;214215		/// Required origin to schedule or cancel calls.216		type ScheduleOrigin: EnsureOrigin<<Self as system::Config>::Origin>;217218		/// Compare the privileges of origins.219		///220		/// This will be used when canceling a task, to ensure that the origin that tries221		/// to cancel has greater or equal privileges as the origin that created the scheduled task.222		///223		/// For simplicity the [`EqualPrivilegeOnly`](frame_support::traits::EqualPrivilegeOnly) can224		/// be used. This will only check if two given origins are equal.225		type OriginPrivilegeCmp: PrivilegeCmp<Self::PalletsOrigin>;226227		/// The maximum number of scheduled calls in the queue for a single block.228		/// Not strictly enforced, but used for weight estimation.229		#[pallet::constant]230		type MaxScheduledPerBlock: Get<u32>;231232		/// Weight information for extrinsics in this pallet.233		type WeightInfo: WeightInfo;234235		/// The preimage provider with which we look up call hashes to get the call.236		type PreimageProvider: PreimageProviderAndMaybeRecipient<Self::Hash>;237238		/// If `Some` then the number of blocks to postpone execution for when the item is delayed.239		type NoPreimagePostponement: Get<Option<Self::BlockNumber>>;240241		/// Sponsoring function.242		// type SponsorshipHandler: SponsorshipHandler<Self::AccountId, <Self as Config>::Call>;243244		/// The helper type used for custom transaction fee logic.245		type CallExecutor: DispatchCall<Self, H160>;246	}247248	/// A Scheduler-Runtime interface for finer payment handling.249	pub trait DispatchCall<T: frame_system::Config + Config, SelfContainedSignedInfo> {250		fn reserve_balance(251			id: ScheduledId,252			sponsor: <T as frame_system::Config>::AccountId,253			call: <T as Config>::Call,254			count: u32,255		) -> Result<(), DispatchError>;256257		fn pay_for_call(258			id: ScheduledId,259			sponsor: <T as frame_system::Config>::AccountId,260			call: <T as Config>::Call,261		) -> Result<u128, DispatchError>;262263		/// Resolve the call dispatch, including any post-dispatch operations.264		fn dispatch_call(265			signer: T::AccountId,266			function: <T as Config>::Call,267		) -> Result<268			Result<PostDispatchInfo, DispatchErrorWithPostInfo<PostDispatchInfo>>,269			TransactionValidityError,270		>;271272		fn cancel_reserve(273			id: ScheduledId,274			sponsor: <T as frame_system::Config>::AccountId,275		) -> Result<u128, DispatchError>;276	}277278	/// Items to be executed, indexed by the block number that they should be executed on.279	#[pallet::storage]280	pub type Agenda<T: Config> =281		StorageMap<_, Twox64Concat, T::BlockNumber, Vec<Option<ScheduledV3Of<T>>>, ValueQuery>;282283	/// Lookup from identity to the block number and index of the task.284	#[pallet::storage]285	pub(crate) type Lookup<T: Config> =286		StorageMap<_, Twox64Concat, ScheduledId, TaskAddress<T::BlockNumber>>;287288	/// Events type.289	#[pallet::event]290	#[pallet::generate_deposit(pub(super) fn deposit_event)]291	pub enum Event<T: Config> {292		/// Scheduled some task.293		Scheduled { when: T::BlockNumber, index: u32 },294		/// Canceled some task.295		Canceled { when: T::BlockNumber, index: u32 },296		/// Dispatched some task.297		Dispatched {298			task: TaskAddress<T::BlockNumber>,299			id: Option<ScheduledId>,300			result: DispatchResult,301		},302		/// The call for the provided hash was not found so the task has been aborted.303		CallLookupFailed {304			task: TaskAddress<T::BlockNumber>,305			id: Option<ScheduledId>,306			error: LookupError,307		},308	}309310	#[pallet::error]311	pub enum Error<T> {312		/// Failed to schedule a call313		FailedToSchedule,314		/// Cannot find the scheduled call.315		NotFound,316		/// Given target block number is in the past.317		TargetBlockNumberInPast,318		/// Reschedule failed because it does not change scheduled time.319		RescheduleNoChange,320	}321322	#[pallet::hooks]323	impl<T: Config> Hooks<BlockNumberFor<T>> for Pallet<T> {324		/// Execute the scheduled calls325		fn on_initialize(now: T::BlockNumber) -> Weight {326			let limit = T::MaximumWeight::get();327328			let mut queued = Agenda::<T>::take(now)329				.into_iter()330				.enumerate()331				.filter_map(|(index, s)| Some((index as u32, s?)))332				.collect::<Vec<_>>();333334			if queued.len() as u32 > T::MaxScheduledPerBlock::get() {335				log::warn!(336					target: "runtime::scheduler",337					"Warning: This block has more items queued in Scheduler than \338					expected from the runtime configuration. An update might be needed."339				);340			}341342			queued.sort_by_key(|(_, s)| s.priority);343344			let next = now + One::one();345346			let mut total_weight: Weight = T::WeightInfo::on_initialize(0);347			for (order, (index, mut s)) in queued.into_iter().enumerate() {348				let named = if let Some(ref id) = s.maybe_id {349					Lookup::<T>::remove(id);350					true351				} else {352					false353				};354355				let (call, maybe_completed) = s.call.resolved::<T::PreimageProvider>();356				s.call = call;357358				let resolved = if let Some(completed) = maybe_completed {359					T::PreimageProvider::unrequest_preimage(&completed);360					true361				} else {362					false363				};364				let call = match s.call.as_value().cloned() {365					Some(c) => c,366					None => {367						// Preimage not available - postpone until some block.368						total_weight.saturating_accrue(T::WeightInfo::item(false, named, None));369						if let Some(delay) = T::NoPreimagePostponement::get() {370							let until = now.saturating_add(delay);371							if let Some(ref id) = s.maybe_id {372								let index = Agenda::<T>::decode_len(until).unwrap_or(0);373								Lookup::<T>::insert(id, (until, index as u32));374							}375							Agenda::<T>::append(until, Some(s));376						}377						continue;378					}379				};380381				let periodic = s.maybe_periodic.is_some();382				let call_weight = call.get_dispatch_info().weight;383				let mut item_weight = T::WeightInfo::item(periodic, named, Some(resolved));384				let origin =385					<<T as Config>::Origin as From<T::PalletsOrigin>>::from(s.origin.clone())386						.into();387				if ensure_signed(origin).is_ok() {388					// Weights of Signed dispatches expect their signing account to be whitelisted.389					item_weight.saturating_accrue(T::DbWeight::get().reads_writes(1, 1));390				}391392				// We allow a scheduled call if any is true:393				// - It's priority is `HARD_DEADLINE`394				// - It does not push the weight past the limit.395				// - It is the first item in the schedule396				let hard_deadline = s.priority <= schedule::HARD_DEADLINE;397				let test_weight = total_weight398					.saturating_add(call_weight)399					.saturating_add(item_weight);400				if !hard_deadline && order > 0 && test_weight > limit {401					// Cannot be scheduled this block - postpone until next.402					total_weight.saturating_accrue(T::WeightInfo::item(false, named, None));403					if let Some(ref id) = s.maybe_id {404						// NOTE: We could reasonably not do this (in which case there would be one405						// block where the named and delayed item could not be referenced by name),406						// but we will do it anyway since it should be mostly free in terms of407						// weight and it is slightly cleaner.408						let index = Agenda::<T>::decode_len(next).unwrap_or(0);409						Lookup::<T>::insert(id, (next, index as u32));410					}411					Agenda::<T>::append(next, Some(s));412					continue;413				}414415				let sender = ensure_signed(416					<<T as Config>::Origin as From<T::PalletsOrigin>>::from(s.origin.clone())417						.into(),418				)419				.unwrap();420421				// // if call have id it was be reserved422				// if s.maybe_id.is_some() {423				// 	let _ = T::CallExecutor::pay_for_call(424				// 		s.maybe_id.unwrap(),425				// 		sender.clone(),426				// 		call.clone(),427				// 	);428				// }429430				let r = T::CallExecutor::dispatch_call(sender, call.clone());431432				let mut actual_call_weight: Weight = item_weight;433				let result: Result<_, DispatchError> = match r {434					Ok(o) => match o {435						Ok(di) => {436							actual_call_weight = di.actual_weight.unwrap_or(item_weight);437							Ok(())438						}439						Err(err) => Err(err.error),440					},441					Err(_) => {442						log::error!(443							target: "runtime::scheduler",444							"Warning: Scheduler has failed to execute a post-dispatch transaction. \445							This block might have become invalid.");446						Err(DispatchError::CannotLookup)447					} // todo possibly force a skip/return here, do something with the error448				};449450				total_weight.saturating_accrue(item_weight);451				total_weight.saturating_accrue(actual_call_weight);452453				Self::deposit_event(Event::Dispatched {454					task: (now, index),455					id: s.maybe_id.clone(),456					result,457				});458459				if let &Some((period, count)) = &s.maybe_periodic {460					if count > 1 {461						s.maybe_periodic = Some((period, count - 1));462					} else {463						s.maybe_periodic = None;464					}465					let wake = now + period;466					// If scheduled is named, place its information in `Lookup`467					if let Some(ref id) = s.maybe_id {468						let wake_index = Agenda::<T>::decode_len(wake).unwrap_or(0);469						Lookup::<T>::insert(id, (wake, wake_index as u32));470					}471					Agenda::<T>::append(wake, Some(s));472				}473			}474			0475			//total_weight476		}477	}478479	#[pallet::call]480	impl<T: Config> Pallet<T> {481		/// Schedule a named task.482		#[pallet::weight(<T as Config>::WeightInfo::schedule_named(T::MaxScheduledPerBlock::get()))]483		pub fn schedule_named(484			origin: OriginFor<T>,485			id: ScheduledId,486			when: T::BlockNumber,487			maybe_periodic: Option<schedule::Period<T::BlockNumber>>,488			priority: schedule::Priority,489			call: Box<CallOrHashOf<T>>,490		) -> DispatchResult {491			T::ScheduleOrigin::ensure_origin(origin.clone())?;492			let origin = <T as Config>::Origin::from(origin);493			Self::do_schedule_named(494				id,495				DispatchTime::At(when),496				maybe_periodic,497				priority,498				origin.caller().clone(),499				*call,500			)?;501			Ok(())502		}503504		/// Cancel a named scheduled task.505		#[pallet::weight(<T as Config>::WeightInfo::cancel_named(T::MaxScheduledPerBlock::get()))]506		pub fn cancel_named(origin: OriginFor<T>, id: ScheduledId) -> DispatchResult {507			T::ScheduleOrigin::ensure_origin(origin.clone())?;508			let origin = <T as Config>::Origin::from(origin);509			Self::do_cancel_named(Some(origin.caller().clone()), id)?;510			Ok(())511		}512513		/// Schedule a named task after a delay.514		///515		/// # <weight>516		/// Same as [`schedule_named`](Self::schedule_named).517		/// # </weight>518		#[pallet::weight(<T as Config>::WeightInfo::schedule_named(T::MaxScheduledPerBlock::get()))]519		pub fn schedule_named_after(520			origin: OriginFor<T>,521			id: ScheduledId,522			after: T::BlockNumber,523			maybe_periodic: Option<schedule::Period<T::BlockNumber>>,524			priority: schedule::Priority,525			call: Box<CallOrHashOf<T>>,526		) -> DispatchResult {527			T::ScheduleOrigin::ensure_origin(origin.clone())?;528			let origin = <T as Config>::Origin::from(origin);529			Self::do_schedule_named(530				id,531				DispatchTime::After(after),532				maybe_periodic,533				priority,534				origin.caller().clone(),535				*call,536			)?;537			Ok(())538		}539	}540}541542impl<T: Config> Pallet<T> {543	#[cfg(feature = "try-runtime")]544	pub fn pre_migrate_to_v3() -> Result<(), &'static str> {545		Ok(())546	}547548	#[cfg(feature = "try-runtime")]549	pub fn post_migrate_to_v3() -> Result<(), &'static str> {550		use frame_support::dispatch::GetStorageVersion;551552		assert!(Self::current_storage_version() == 3);553		for k in Agenda::<T>::iter_keys() {554			let _ = Agenda::<T>::try_get(k).map_err(|()| "Invalid item in Agenda")?;555		}556		Ok(())557	}558559	/// Helper to migrate scheduler when the pallet origin type has changed.560	pub fn migrate_origin<OldOrigin: Into<T::PalletsOrigin> + codec::Decode>() {561		Agenda::<T>::translate::<562			Vec<Option<Scheduled<CallOrHashOf<T>, T::BlockNumber, OldOrigin, T::AccountId>>>,563			_,564		>(|_, agenda| {565			Some(566				agenda567					.into_iter()568					.map(|schedule| {569						schedule.map(|schedule| Scheduled {570							maybe_id: schedule.maybe_id,571							priority: schedule.priority,572							call: schedule.call,573							maybe_periodic: schedule.maybe_periodic,574							origin: schedule.origin.into(),575							_phantom: Default::default(),576						})577					})578					.collect::<Vec<_>>(),579			)580		});581	}582583	fn resolve_time(when: DispatchTime<T::BlockNumber>) -> Result<T::BlockNumber, DispatchError> {584		let now = frame_system::Pallet::<T>::block_number();585586		let when = match when {587			DispatchTime::At(x) => x,588			// The current block has already completed it's scheduled tasks, so589			// Schedule the task at lest one block after this current block.590			DispatchTime::After(x) => now.saturating_add(x).saturating_add(One::one()),591		};592593		if when <= now {594			return Err(Error::<T>::TargetBlockNumberInPast.into());595		}596597		Ok(when)598	}599600	fn do_schedule(601		when: DispatchTime<T::BlockNumber>,602		maybe_periodic: Option<schedule::Period<T::BlockNumber>>,603		priority: schedule::Priority,604		origin: T::PalletsOrigin,605		call: CallOrHashOf<T>,606	) -> Result<TaskAddress<T::BlockNumber>, DispatchError> {607		let when = Self::resolve_time(when)?;608		call.ensure_requested::<T::PreimageProvider>();609610		// sanitize maybe_periodic611		let maybe_periodic = maybe_periodic612			.filter(|p| p.1 > 1 && !p.0.is_zero())613			// Remove one from the number of repetitions since we will schedule one now.614			.map(|(p, c)| (p, c - 1));615		let s = Some(Scheduled {616			maybe_id: None,617			priority,618			call,619			maybe_periodic,620			origin,621			_phantom: PhantomData::<T::AccountId>::default(),622		});623		Agenda::<T>::append(when, s);624		let index = Agenda::<T>::decode_len(when).unwrap_or(1) as u32 - 1;625		Self::deposit_event(Event::Scheduled { when, index });626627		Ok((when, index))628	}629630	fn do_cancel(631		origin: Option<T::PalletsOrigin>,632		(when, index): TaskAddress<T::BlockNumber>,633	) -> Result<(), DispatchError> {634		let scheduled = Agenda::<T>::try_mutate(when, |agenda| {635			agenda.get_mut(index as usize).map_or(636				Ok(None),637				|s| -> Result<Option<Scheduled<_, _, _, _>>, DispatchError> {638					if let (Some(ref o), Some(ref s)) = (origin, s.borrow()) {639						if matches!(640							T::OriginPrivilegeCmp::cmp_privilege(o, &s.origin),641							Some(Ordering::Less) | None642						) {643							return Err(BadOrigin.into());644						}645					};646					Ok(s.take())647				},648			)649		})?;650		if let Some(s) = scheduled {651			s.call.ensure_unrequested::<T::PreimageProvider>();652			if let Some(id) = s.maybe_id {653				Lookup::<T>::remove(id);654			}655			Self::deposit_event(Event::Canceled { when, index });656			Ok(())657		} else {658			Err(Error::<T>::NotFound)?659		}660	}661662	fn do_reschedule(663		(when, index): TaskAddress<T::BlockNumber>,664		new_time: DispatchTime<T::BlockNumber>,665	) -> Result<TaskAddress<T::BlockNumber>, DispatchError> {666		let new_time = Self::resolve_time(new_time)?;667668		if new_time == when {669			return Err(Error::<T>::RescheduleNoChange.into());670		}671672		Agenda::<T>::try_mutate(when, |agenda| -> DispatchResult {673			let task = agenda.get_mut(index as usize).ok_or(Error::<T>::NotFound)?;674			let task = task.take().ok_or(Error::<T>::NotFound)?;675			Agenda::<T>::append(new_time, Some(task));676			Ok(())677		})?;678679		let new_index = Agenda::<T>::decode_len(new_time).unwrap_or(1) as u32 - 1;680		Self::deposit_event(Event::Canceled { when, index });681		Self::deposit_event(Event::Scheduled {682			when: new_time,683			index: new_index,684		});685686		Ok((new_time, new_index))687	}688689	fn do_schedule_named(690		id: ScheduledId,691		when: DispatchTime<T::BlockNumber>,692		maybe_periodic: Option<schedule::Period<T::BlockNumber>>,693		priority: schedule::Priority,694		origin: T::PalletsOrigin,695		call: CallOrHashOf<T>,696	) -> Result<TaskAddress<T::BlockNumber>, DispatchError> {697		// ensure id it is unique698		if Lookup::<T>::contains_key(&id) {699			return Err(Error::<T>::FailedToSchedule)?;700		}701702		let when = Self::resolve_time(when)?;703704		call.ensure_requested::<T::PreimageProvider>();705706		// sanitize maybe_periodic707		let maybe_periodic = maybe_periodic708			.filter(|p| p.1 > 1 && !p.0.is_zero())709			// Remove one from the number of repetitions since we will schedule one now.710			.map(|(p, c)| (p, c - 1));711712		let s = Scheduled {713			maybe_id: Some(id.clone()),714			priority,715			call: call.clone(),716			maybe_periodic,717			origin: origin.clone(),718			_phantom: Default::default(),719		};720721		// reserve balance for periodic execution722		// let sender =723		// 	ensure_signed(<<T as Config>::Origin as From<T::PalletsOrigin>>::from(origin).into())?;724		// let repeats = match maybe_periodic {725		// 	Some(p) => p.1,726		// 	None => 1,727		// };728		// let _ = T::CallExecutor::reserve_balance(729		// 	id.clone(),730		// 	sender,731		// 	call.as_value().unwrap().clone(),732		// 	repeats,733		// );734735		Agenda::<T>::append(when, Some(s));736		let index = Agenda::<T>::decode_len(when).unwrap_or(1) as u32 - 1;737		let address = (when, index);738		Lookup::<T>::insert(&id, &address);739		Self::deposit_event(Event::Scheduled { when, index });740741		Ok(address)742	}743744	fn do_cancel_named(origin: Option<T::PalletsOrigin>, id: ScheduledId) -> DispatchResult {745		Lookup::<T>::try_mutate_exists(id, |lookup| -> DispatchResult {746			if let Some((when, index)) = lookup.take() {747				let i = index as usize;748				Agenda::<T>::try_mutate(when, |agenda| -> DispatchResult {749					if let Some(s) = agenda.get_mut(i) {750						if let (Some(ref o), Some(ref s)) = (origin.clone(), s.borrow()) {751							if matches!(752								T::OriginPrivilegeCmp::cmp_privilege(o, &s.origin),753								Some(Ordering::Less) | None754							) {755								return Err(BadOrigin.into());756							}757							// release balance reserve758							// let sender = ensure_signed(759							// 	<<T as Config>::Origin as From<T::PalletsOrigin>>::from(760							// 		origin.unwrap(),761							// 	)762							// 	.into(),763							// )?;764							// let _ = T::CallExecutor::cancel_reserve(id, sender);765766							s.call.ensure_unrequested::<T::PreimageProvider>();767						}768						*s = None;769					}770					Ok(())771				})?;772773				Self::deposit_event(Event::Canceled { when, index });774				Ok(())775			} else {776				Err(Error::<T>::NotFound)?777			}778		})779	}780781	fn do_reschedule_named(782		id: ScheduledId,783		new_time: DispatchTime<T::BlockNumber>,784	) -> Result<TaskAddress<T::BlockNumber>, DispatchError> {785		let new_time = Self::resolve_time(new_time)?;786787		Lookup::<T>::try_mutate_exists(788			id,789			|lookup| -> Result<TaskAddress<T::BlockNumber>, DispatchError> {790				let (when, index) = lookup.ok_or(Error::<T>::NotFound)?;791792				if new_time == when {793					return Err(Error::<T>::RescheduleNoChange.into());794				}795796				Agenda::<T>::try_mutate(when, |agenda| -> DispatchResult {797					let task = agenda.get_mut(index as usize).ok_or(Error::<T>::NotFound)?;798					let task = task.take().ok_or(Error::<T>::NotFound)?;799					Agenda::<T>::append(new_time, Some(task));800801					Ok(())802				})?;803804				let new_index = Agenda::<T>::decode_len(new_time).unwrap_or(1) as u32 - 1;805				Self::deposit_event(Event::Canceled { when, index });806				Self::deposit_event(Event::Scheduled {807					when: new_time,808					index: new_index,809				});810811				*lookup = Some((new_time, new_index));812813				Ok((new_time, new_index))814			},815		)816	}817}818819impl<T: Config> schedule::v2::Anon<T::BlockNumber, <T as Config>::Call, T::PalletsOrigin>820	for Pallet<T>821{822	type Address = TaskAddress<T::BlockNumber>;823	type Hash = T::Hash;824825	fn schedule(826		when: DispatchTime<T::BlockNumber>,827		maybe_periodic: Option<schedule::Period<T::BlockNumber>>,828		priority: schedule::Priority,829		origin: T::PalletsOrigin,830		call: CallOrHashOf<T>,831	) -> Result<Self::Address, DispatchError> {832		Self::do_schedule(when, maybe_periodic, priority, origin, call)833	}834835	fn cancel((when, index): Self::Address) -> Result<(), ()> {836		Self::do_cancel(None, (when, index)).map_err(|_| ())837	}838839	fn reschedule(840		address: Self::Address,841		when: DispatchTime<T::BlockNumber>,842	) -> Result<Self::Address, DispatchError> {843		Self::do_reschedule(address, when)844	}845846	fn next_dispatch_time((when, index): Self::Address) -> Result<T::BlockNumber, ()> {847		Agenda::<T>::get(when)848			.get(index as usize)849			.ok_or(())850			.map(|_| when)851	}852}853854impl<T: Config> schedule::v2::Named<T::BlockNumber, <T as Config>::Call, T::PalletsOrigin>855	for Pallet<T>856{857	type Address = TaskAddress<T::BlockNumber>;858	type Hash = T::Hash;859860	fn schedule_named(861		id: Vec<u8>,862		when: DispatchTime<T::BlockNumber>,863		maybe_periodic: Option<schedule::Period<T::BlockNumber>>,864		priority: schedule::Priority,865		origin: T::PalletsOrigin,866		call: CallOrHashOf<T>,867	) -> Result<Self::Address, ()> {868		let inner_id: ScheduledId = id869			.try_into()870			.unwrap_or([0; MAX_TASK_ID_LENGTH_IN_BYTES as usize]);871		Self::do_schedule_named(inner_id, when, maybe_periodic, priority, origin, call)872			.map_err(|_| ())873	}874875	fn cancel_named(id: Vec<u8>) -> Result<(), ()> {876		let inner_id: ScheduledId = id877			.try_into()878			.unwrap_or([0; MAX_TASK_ID_LENGTH_IN_BYTES as usize]);879		Self::do_cancel_named(None, inner_id).map_err(|_| ())880	}881882	fn reschedule_named(883		id: Vec<u8>,884		when: DispatchTime<T::BlockNumber>,885	) -> Result<Self::Address, DispatchError> {886		let inner_id: ScheduledId = id887			.try_into()888			.unwrap_or([0; MAX_TASK_ID_LENGTH_IN_BYTES as usize]);889		Self::do_reschedule_named(inner_id, when)890	}891892	fn next_dispatch_time(id: Vec<u8>) -> Result<T::BlockNumber, ()> {893		let inner_id: ScheduledId = id894			.try_into()895			.unwrap_or([0; MAX_TASK_ID_LENGTH_IN_BYTES as usize]);896		Lookup::<T>::get(inner_id)897			.and_then(|(when, index)| Agenda::<T>::get(when).get(index as usize).map(|_| when))898			.ok_or(())899	}900}