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

difftreelog

source

pallets/scheduler/src/lib.rs28.0 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//! # Schedulerdo_reschedule19//!20//! This Pallet exposes capabilities for scheduling dispatches to occur at a21//! specified block number or at a specified period. These scheduled dispatches22//! may be named or anonymous and may be canceled.23//!24//! **NOTE:** The scheduled calls will be dispatched with the default filter25//! for the origin: namely `frame_system::Config::BaseCallFilter` for all origin26//! except root which will get no filter. And not the filter contained in origin27//! use to call `fn schedule`.28//!29//! If a call is scheduled using proxy or whatever mecanism which adds filter,30//! then those filter will not be used when dispatching the schedule call.31//!32//! ## Interface33//!34//! ### Dispatchable Functions35//!36//! * `schedule` - schedule a dispatch, which may be periodic, to occur at a specified block and37//!   with a specified priority.38//! * `cancel` - cancel a scheduled dispatch, specified by block number and index.39//! * `schedule_named` - augments the `schedule` interface with an additional `Vec<u8>` parameter40//!   that can be used for identification.41//! * `cancel_named` - the named complement to the cancel function.4243// Ensure we're `no_std` when compiling for Wasm.44#![cfg_attr(not(feature = "std"), no_std)]4546// FIXME47// #[cfg(feature = "runtime-benchmarks")]48// mod benchmarking;4950pub mod weights;5152use sp_core::H160;53use codec::{Codec, Decode, Encode};54use frame_system::{self as system, ensure_signed};55pub use pallet::*;56use scale_info::TypeInfo;57use sp_runtime::{58	traits::{BadOrigin, One, Saturating, Zero},59	RuntimeDebug, DispatchErrorWithPostInfo,60};61use sp_std::{borrow::Borrow, cmp::Ordering, marker::PhantomData, prelude::*};6263use frame_support::{64	dispatch::{DispatchError, DispatchResult, Dispatchable, Parameter},65	traits::{66		schedule::{self, DispatchTime, MaybeHashed},67		NamedReservableCurrency, EnsureOrigin, Get, IsType, OriginTrait, PrivilegeCmp,68		StorageVersion,69	},70	weights::{GetDispatchInfo, Weight},71};7273pub use weights::WeightInfo;7475/// Just a simple index for naming period tasks.76pub type PeriodicIndex = u32;77/// The location of a scheduled task that can be used to remove it.78pub type TaskAddress<BlockNumber> = (BlockNumber, u32);79pub const MAX_TASK_ID_LENGTH_IN_BYTES: u8 = 16;8081type ScheduledId = [u8; MAX_TASK_ID_LENGTH_IN_BYTES as usize];82pub type CallOrHashOf<T> = MaybeHashed<<T as Config>::Call, <T as frame_system::Config>::Hash>;8384/// Information regarding an item to be executed in the future.85#[cfg_attr(any(feature = "std", test), derive(PartialEq, Eq))]86#[derive(Clone, RuntimeDebug, Encode, Decode, TypeInfo)]87pub struct ScheduledV3<Call, BlockNumber, PalletsOrigin, AccountId> {88	/// The unique identity for this task, if there is one.89	maybe_id: Option<ScheduledId>,90	/// This task's priority.91	priority: schedule::Priority,92	/// The call to be dispatched.93	call: Call,94	/// If the call is periodic, then this points to the information concerning that.95	maybe_periodic: Option<schedule::Period<BlockNumber>>,96	/// The origin to dispatch the call.97	origin: PalletsOrigin,98	_phantom: PhantomData<AccountId>,99}100101pub type ScheduledV3Of<T> = ScheduledV3<102	CallOrHashOf<T>,103	<T as frame_system::Config>::BlockNumber,104	<T as Config>::PalletsOrigin,105	<T as frame_system::Config>::AccountId,106>;107108pub type ScheduledOf<T> = ScheduledV3Of<T>;109110/// The current version of Scheduled struct.111pub type Scheduled<Call, BlockNumber, PalletsOrigin, AccountId> =112	ScheduledV3<Call, BlockNumber, PalletsOrigin, AccountId>;113114#[cfg(feature = "runtime-benchmarks")]115mod preimage_provider {116	use frame_support::traits::PreimageRecipient;117	pub trait PreimageProviderAndMaybeRecipient<H>: PreimageRecipient<H> {}118	impl<H, T: PreimageRecipient<H>> PreimageProviderAndMaybeRecipient<H> for T {}119}120121#[cfg(not(feature = "runtime-benchmarks"))]122mod preimage_provider {123	use frame_support::traits::PreimageProvider;124	pub trait PreimageProviderAndMaybeRecipient<H>: PreimageProvider<H> {}125	impl<H, T: PreimageProvider<H>> PreimageProviderAndMaybeRecipient<H> for T {}126}127128pub use preimage_provider::PreimageProviderAndMaybeRecipient;129130pub(crate) trait MarginalWeightInfo: WeightInfo {131	fn item(periodic: bool, named: bool, resolved: Option<bool>) -> Weight {132		match (periodic, named, resolved) {133			(_, false, None) => Self::on_initialize_aborted(2) - Self::on_initialize_aborted(1),134			(_, true, None) => {135				Self::on_initialize_named_aborted(2) - Self::on_initialize_named_aborted(1)136			}137			(false, false, Some(false)) => Self::on_initialize(2) - Self::on_initialize(1),138			(false, true, Some(false)) => {139				Self::on_initialize_named(2) - Self::on_initialize_named(1)140			}141			(true, false, Some(false)) => {142				Self::on_initialize_periodic(2) - Self::on_initialize_periodic(1)143			}144			(true, true, Some(false)) => {145				Self::on_initialize_periodic_named(2) - Self::on_initialize_periodic_named(1)146			}147			(false, false, Some(true)) => {148				Self::on_initialize_resolved(2) - Self::on_initialize_resolved(1)149			}150			(false, true, Some(true)) => {151				Self::on_initialize_named_resolved(2) - Self::on_initialize_named_resolved(1)152			}153			(true, false, Some(true)) => {154				Self::on_initialize_periodic_resolved(2) - Self::on_initialize_periodic_resolved(1)155			}156			(true, true, Some(true)) => {157				Self::on_initialize_periodic_named_resolved(2)158					- Self::on_initialize_periodic_named_resolved(1)159			}160		}161	}162}163impl<T: WeightInfo> MarginalWeightInfo for T {}164165#[frame_support::pallet]166pub mod pallet {167	use super::*;168	use frame_support::{169		dispatch::PostDispatchInfo,170		pallet_prelude::*,171		traits::{schedule::LookupError, PreimageProvider},172	};173	use frame_system::pallet_prelude::*;174175	/// The current storage version.176	const STORAGE_VERSION: StorageVersion = StorageVersion::new(3);177178	#[pallet::pallet]179	#[pallet::generate_store(pub(super) trait Store)]180	#[pallet::storage_version(STORAGE_VERSION)]181	#[pallet::without_storage_info]182	pub struct Pallet<T>(_);183184	/// `system::Config` should always be included in our implied traits.185	#[pallet::config]186	pub trait Config: frame_system::Config {187		/// The overarching event type.188		type Event: From<Event<Self>> + IsType<<Self as frame_system::Config>::Event>;189190		/// The aggregated origin which the dispatch will take.191		type Origin: OriginTrait<PalletsOrigin = Self::PalletsOrigin>192			+ From<Self::PalletsOrigin>193			+ IsType<<Self as system::Config>::Origin>;194195		/// The caller origin, overarching type of all pallets origins.196		type PalletsOrigin: From<system::RawOrigin<Self::AccountId>> + Codec + Clone + Eq + TypeInfo;197198		type Currency: NamedReservableCurrency<Self::AccountId, ReserveIdentifier = ScheduledId>;199200		/// The aggregated call type.201		type Call: Parameter202			+ Dispatchable<Origin = <Self as Config>::Origin, PostInfo = PostDispatchInfo>203			+ GetDispatchInfo204			+ From<system::Call<Self>>;205206		/// The maximum weight that may be scheduled per block for any dispatchables of less207		/// priority than `schedule::HARD_DEADLINE`.208		#[pallet::constant]209		type MaximumWeight: Get<Weight>;210211		/// Required origin to schedule or cancel calls.212		type ScheduleOrigin: EnsureOrigin<<Self as system::Config>::Origin>;213214		/// Compare the privileges of origins.215		///216		/// This will be used when canceling a task, to ensure that the origin that tries217		/// to cancel has greater or equal privileges as the origin that created the scheduled task.218		///219		/// For simplicity the [`EqualPrivilegeOnly`](frame_support::traits::EqualPrivilegeOnly) can220		/// be used. This will only check if two given origins are equal.221		type OriginPrivilegeCmp: PrivilegeCmp<Self::PalletsOrigin>;222223		/// The maximum number of scheduled calls in the queue for a single block.224		/// Not strictly enforced, but used for weight estimation.225		#[pallet::constant]226		type MaxScheduledPerBlock: Get<u32>;227228		/// Weight information for extrinsics in this pallet.229		type WeightInfo: WeightInfo;230231		/// The preimage provider with which we look up call hashes to get the call.232		type PreimageProvider: PreimageProviderAndMaybeRecipient<Self::Hash>;233234		/// If `Some` then the number of blocks to postpone execution for when the item is delayed.235		type NoPreimagePostponement: Get<Option<Self::BlockNumber>>;236237		/// Sponsoring function.238		// type SponsorshipHandler: SponsorshipHandler<Self::AccountId, <Self as Config>::Call>;239240		/// The helper type used for custom transaction fee logic.241		type CallExecutor: DispatchCall<Self, H160>;242	}243244	/// A Scheduler-Runtime interface for finer payment handling.245	pub trait DispatchCall<T: frame_system::Config + Config, SelfContainedSignedInfo> {246		fn reserve_balance(247			id: ScheduledId,248			sponsor: <T as frame_system::Config>::AccountId,249			call: <T as Config>::Call,250			count: u32,251		) -> Result<(), DispatchError>;252253		fn pay_for_call(254			id: ScheduledId,255			sponsor: <T as frame_system::Config>::AccountId,256			call: <T as Config>::Call,257		) -> Result<u128, DispatchError>;258259		/// Resolve the call dispatch, including any post-dispatch operations.260		fn dispatch_call(261			signer: T::AccountId,262			function: <T as Config>::Call,263		) -> Result<264			Result<PostDispatchInfo, DispatchErrorWithPostInfo<PostDispatchInfo>>,265			TransactionValidityError,266		>;267268		fn cancel_reserve(269			id: ScheduledId,270			sponsor: <T as frame_system::Config>::AccountId,271		) -> Result<u128, DispatchError>;272	}273274	/// Items to be executed, indexed by the block number that they should be executed on.275	#[pallet::storage]276	pub type Agenda<T: Config> =277		StorageMap<_, Twox64Concat, T::BlockNumber, Vec<Option<ScheduledV3Of<T>>>, ValueQuery>;278279	/// Lookup from identity to the block number and index of the task.280	#[pallet::storage]281	pub(crate) type Lookup<T: Config> =282		StorageMap<_, Twox64Concat, ScheduledId, TaskAddress<T::BlockNumber>>;283284	/// Events type.285	#[pallet::event]286	#[pallet::generate_deposit(pub(super) fn deposit_event)]287	pub enum Event<T: Config> {288		/// Scheduled some task.289		Scheduled { when: T::BlockNumber, index: u32 },290		/// Canceled some task.291		Canceled { when: T::BlockNumber, index: u32 },292		/// Dispatched some task.293		Dispatched {294			task: TaskAddress<T::BlockNumber>,295			id: Option<ScheduledId>,296			result: DispatchResult,297		},298		/// The call for the provided hash was not found so the task has been aborted.299		CallLookupFailed {300			task: TaskAddress<T::BlockNumber>,301			id: Option<ScheduledId>,302			error: LookupError,303		},304	}305306	#[pallet::error]307	pub enum Error<T> {308		/// Failed to schedule a call309		FailedToSchedule,310		/// Cannot find the scheduled call.311		NotFound,312		/// Given target block number is in the past.313		TargetBlockNumberInPast,314		/// Reschedule failed because it does not change scheduled time.315		RescheduleNoChange,316	}317318	#[pallet::hooks]319	impl<T: Config> Hooks<BlockNumberFor<T>> for Pallet<T> {320		/// Execute the scheduled calls321		fn on_initialize(now: T::BlockNumber) -> Weight {322			let limit = T::MaximumWeight::get();323324			let mut queued = Agenda::<T>::take(now)325				.into_iter()326				.enumerate()327				.filter_map(|(index, s)| Some((index as u32, s?)))328				.collect::<Vec<_>>();329330			if queued.len() as u32 > T::MaxScheduledPerBlock::get() {331				log::warn!(332					target: "runtime::scheduler",333					"Warning: This block has more items queued in Scheduler than \334					expected from the runtime configuration. An update might be needed."335				);336			}337338			queued.sort_by_key(|(_, s)| s.priority);339340			let next = now + One::one();341342			let mut total_weight: Weight = T::WeightInfo::on_initialize(0);343			for (order, (index, mut s)) in queued.into_iter().enumerate() {344				let named = if let Some(ref id) = s.maybe_id {345					Lookup::<T>::remove(id);346					true347				} else {348					false349				};350351				let (call, maybe_completed) = s.call.resolved::<T::PreimageProvider>();352				s.call = call;353354				let resolved = if let Some(completed) = maybe_completed {355					T::PreimageProvider::unrequest_preimage(&completed);356					true357				} else {358					false359				};360				let call = match s.call.as_value().cloned() {361					Some(c) => c,362					None => {363						// Preimage not available - postpone until some block.364						total_weight.saturating_accrue(T::WeightInfo::item(false, named, None));365						if let Some(delay) = T::NoPreimagePostponement::get() {366							let until = now.saturating_add(delay);367							if let Some(ref id) = s.maybe_id {368								let index = Agenda::<T>::decode_len(until).unwrap_or(0);369								Lookup::<T>::insert(id, (until, index as u32));370							}371							Agenda::<T>::append(until, Some(s));372						}373						continue;374					}375				};376377				let periodic = s.maybe_periodic.is_some();378				let call_weight = call.get_dispatch_info().weight;379				let mut item_weight = T::WeightInfo::item(periodic, named, Some(resolved));380				let origin =381					<<T as Config>::Origin as From<T::PalletsOrigin>>::from(s.origin.clone())382						.into();383				if ensure_signed(origin).is_ok() {384					// Weights of Signed dispatches expect their signing account to be whitelisted.385					item_weight.saturating_accrue(T::DbWeight::get().reads_writes(1, 1));386				}387388				// We allow a scheduled call if any is true:389				// - It's priority is `HARD_DEADLINE`390				// - It does not push the weight past the limit.391				// - It is the first item in the schedule392				let hard_deadline = s.priority <= schedule::HARD_DEADLINE;393				let test_weight = total_weight394					.saturating_add(call_weight)395					.saturating_add(item_weight);396				if !hard_deadline && order > 0 && test_weight > limit {397					// Cannot be scheduled this block - postpone until next.398					total_weight.saturating_accrue(T::WeightInfo::item(false, named, None));399					if let Some(ref id) = s.maybe_id {400						// NOTE: We could reasonably not do this (in which case there would be one401						// block where the named and delayed item could not be referenced by name),402						// but we will do it anyway since it should be mostly free in terms of403						// weight and it is slightly cleaner.404						let index = Agenda::<T>::decode_len(next).unwrap_or(0);405						Lookup::<T>::insert(id, (next, index as u32));406					}407					Agenda::<T>::append(next, Some(s));408					continue;409				}410411				let sender = ensure_signed(412					<<T as Config>::Origin as From<T::PalletsOrigin>>::from(s.origin.clone())413						.into(),414				)415				.unwrap();416417				// // if call have id it was be reserved418				// if s.maybe_id.is_some() {419				// 	let _ = T::CallExecutor::pay_for_call(420				// 		s.maybe_id.unwrap(),421				// 		sender.clone(),422				// 		call.clone(),423				// 	);424				// }425426				let r = T::CallExecutor::dispatch_call(sender, call.clone());427428				let mut actual_call_weight: Weight = item_weight;429				let result: Result<_, DispatchError> = match r {430					Ok(o) => match o {431						Ok(di) => {432							actual_call_weight = di.actual_weight.unwrap_or(item_weight);433							Ok(())434						}435						Err(err) => Err(err.error),436					},437					Err(_) => {438						log::error!(439							target: "runtime::scheduler",440							"Warning: Scheduler has failed to execute a post-dispatch transaction. \441							This block might have become invalid.");442						Err(DispatchError::CannotLookup)443					} // todo possibly force a skip/return here, do something with the error444				};445446				total_weight.saturating_accrue(item_weight);447				total_weight.saturating_accrue(actual_call_weight);448449				Self::deposit_event(Event::Dispatched {450					task: (now, index),451					id: s.maybe_id.clone(),452					result,453				});454455				if let &Some((period, count)) = &s.maybe_periodic {456					if count > 1 {457						s.maybe_periodic = Some((period, count - 1));458					} else {459						s.maybe_periodic = None;460					}461					let wake = now + period;462					// If scheduled is named, place its information in `Lookup`463					if let Some(ref id) = s.maybe_id {464						let wake_index = Agenda::<T>::decode_len(wake).unwrap_or(0);465						Lookup::<T>::insert(id, (wake, wake_index as u32));466					}467					Agenda::<T>::append(wake, Some(s));468				}469			}470			0471			//total_weight472		}473	}474475	#[pallet::call]476	impl<T: Config> Pallet<T> {477		/// Schedule a named task.478		#[pallet::weight(<T as Config>::WeightInfo::schedule_named(T::MaxScheduledPerBlock::get()))]479		pub fn schedule_named(480			origin: OriginFor<T>,481			id: ScheduledId,482			when: T::BlockNumber,483			maybe_periodic: Option<schedule::Period<T::BlockNumber>>,484			priority: schedule::Priority,485			call: Box<CallOrHashOf<T>>,486		) -> DispatchResult {487			T::ScheduleOrigin::ensure_origin(origin.clone())?;488			let origin = <T as Config>::Origin::from(origin);489			Self::do_schedule_named(490				id,491				DispatchTime::At(when),492				maybe_periodic,493				priority,494				origin.caller().clone(),495				*call,496			)?;497			Ok(())498		}499500		/// Cancel a named scheduled task.501		#[pallet::weight(<T as Config>::WeightInfo::cancel_named(T::MaxScheduledPerBlock::get()))]502		pub fn cancel_named(origin: OriginFor<T>, id: ScheduledId) -> DispatchResult {503			T::ScheduleOrigin::ensure_origin(origin.clone())?;504			let origin = <T as Config>::Origin::from(origin);505			Self::do_cancel_named(Some(origin.caller().clone()), id)?;506			Ok(())507		}508509		/// Schedule a named task after a delay.510		///511		/// # <weight>512		/// Same as [`schedule_named`](Self::schedule_named).513		/// # </weight>514		#[pallet::weight(<T as Config>::WeightInfo::schedule_named(T::MaxScheduledPerBlock::get()))]515		pub fn schedule_named_after(516			origin: OriginFor<T>,517			id: ScheduledId,518			after: T::BlockNumber,519			maybe_periodic: Option<schedule::Period<T::BlockNumber>>,520			priority: schedule::Priority,521			call: Box<CallOrHashOf<T>>,522		) -> DispatchResult {523			T::ScheduleOrigin::ensure_origin(origin.clone())?;524			let origin = <T as Config>::Origin::from(origin);525			Self::do_schedule_named(526				id,527				DispatchTime::After(after),528				maybe_periodic,529				priority,530				origin.caller().clone(),531				*call,532			)?;533			Ok(())534		}535	}536}537538impl<T: Config> Pallet<T> {539	#[cfg(feature = "try-runtime")]540	pub fn pre_migrate_to_v3() -> Result<(), &'static str> {541		Ok(())542	}543544	#[cfg(feature = "try-runtime")]545	pub fn post_migrate_to_v3() -> Result<(), &'static str> {546		use frame_support::dispatch::GetStorageVersion;547548		assert!(Self::current_storage_version() == 3);549		for k in Agenda::<T>::iter_keys() {550			let _ = Agenda::<T>::try_get(k).map_err(|()| "Invalid item in Agenda")?;551		}552		Ok(())553	}554555	/// Helper to migrate scheduler when the pallet origin type has changed.556	pub fn migrate_origin<OldOrigin: Into<T::PalletsOrigin> + codec::Decode>() {557		Agenda::<T>::translate::<558			Vec<Option<Scheduled<CallOrHashOf<T>, T::BlockNumber, OldOrigin, T::AccountId>>>,559			_,560		>(|_, agenda| {561			Some(562				agenda563					.into_iter()564					.map(|schedule| {565						schedule.map(|schedule| Scheduled {566							maybe_id: schedule.maybe_id,567							priority: schedule.priority,568							call: schedule.call,569							maybe_periodic: schedule.maybe_periodic,570							origin: schedule.origin.into(),571							_phantom: Default::default(),572						})573					})574					.collect::<Vec<_>>(),575			)576		});577	}578579	fn resolve_time(when: DispatchTime<T::BlockNumber>) -> Result<T::BlockNumber, DispatchError> {580		let now = frame_system::Pallet::<T>::block_number();581582		let when = match when {583			DispatchTime::At(x) => x,584			// The current block has already completed it's scheduled tasks, so585			// Schedule the task at lest one block after this current block.586			DispatchTime::After(x) => now.saturating_add(x).saturating_add(One::one()),587		};588589		if when <= now {590			return Err(Error::<T>::TargetBlockNumberInPast.into());591		}592593		Ok(when)594	}595596	fn do_schedule(597		when: DispatchTime<T::BlockNumber>,598		maybe_periodic: Option<schedule::Period<T::BlockNumber>>,599		priority: schedule::Priority,600		origin: T::PalletsOrigin,601		call: CallOrHashOf<T>,602	) -> Result<TaskAddress<T::BlockNumber>, DispatchError> {603		let when = Self::resolve_time(when)?;604		call.ensure_requested::<T::PreimageProvider>();605606		// sanitize maybe_periodic607		let maybe_periodic = maybe_periodic608			.filter(|p| p.1 > 1 && !p.0.is_zero())609			// Remove one from the number of repetitions since we will schedule one now.610			.map(|(p, c)| (p, c - 1));611		let s = Some(Scheduled {612			maybe_id: None,613			priority,614			call,615			maybe_periodic,616			origin,617			_phantom: PhantomData::<T::AccountId>::default(),618		});619		Agenda::<T>::append(when, s);620		let index = Agenda::<T>::decode_len(when).unwrap_or(1) as u32 - 1;621		Self::deposit_event(Event::Scheduled { when, index });622623		Ok((when, index))624	}625626	fn do_cancel(627		origin: Option<T::PalletsOrigin>,628		(when, index): TaskAddress<T::BlockNumber>,629	) -> Result<(), DispatchError> {630		let scheduled = Agenda::<T>::try_mutate(when, |agenda| {631			agenda.get_mut(index as usize).map_or(632				Ok(None),633				|s| -> Result<Option<Scheduled<_, _, _, _>>, DispatchError> {634					if let (Some(ref o), Some(ref s)) = (origin, s.borrow()) {635						if matches!(636							T::OriginPrivilegeCmp::cmp_privilege(o, &s.origin),637							Some(Ordering::Less) | None638						) {639							return Err(BadOrigin.into());640						}641					};642					Ok(s.take())643				},644			)645		})?;646		if let Some(s) = scheduled {647			s.call.ensure_unrequested::<T::PreimageProvider>();648			if let Some(id) = s.maybe_id {649				Lookup::<T>::remove(id);650			}651			Self::deposit_event(Event::Canceled { when, index });652			Ok(())653		} else {654			Err(Error::<T>::NotFound)?655		}656	}657658	fn do_reschedule(659		(when, index): TaskAddress<T::BlockNumber>,660		new_time: DispatchTime<T::BlockNumber>,661	) -> Result<TaskAddress<T::BlockNumber>, DispatchError> {662		let new_time = Self::resolve_time(new_time)?;663664		if new_time == when {665			return Err(Error::<T>::RescheduleNoChange.into());666		}667668		Agenda::<T>::try_mutate(when, |agenda| -> DispatchResult {669			let task = agenda.get_mut(index as usize).ok_or(Error::<T>::NotFound)?;670			let task = task.take().ok_or(Error::<T>::NotFound)?;671			Agenda::<T>::append(new_time, Some(task));672			Ok(())673		})?;674675		let new_index = Agenda::<T>::decode_len(new_time).unwrap_or(1) as u32 - 1;676		Self::deposit_event(Event::Canceled { when, index });677		Self::deposit_event(Event::Scheduled {678			when: new_time,679			index: new_index,680		});681682		Ok((new_time, new_index))683	}684685	fn do_schedule_named(686		id: ScheduledId,687		when: DispatchTime<T::BlockNumber>,688		maybe_periodic: Option<schedule::Period<T::BlockNumber>>,689		priority: schedule::Priority,690		origin: T::PalletsOrigin,691		call: CallOrHashOf<T>,692	) -> Result<TaskAddress<T::BlockNumber>, DispatchError> {693		// ensure id it is unique694		if Lookup::<T>::contains_key(&id) {695			return Err(Error::<T>::FailedToSchedule)?;696		}697698		let when = Self::resolve_time(when)?;699700		call.ensure_requested::<T::PreimageProvider>();701702		// sanitize maybe_periodic703		let maybe_periodic = maybe_periodic704			.filter(|p| p.1 > 1 && !p.0.is_zero())705			// Remove one from the number of repetitions since we will schedule one now.706			.map(|(p, c)| (p, c - 1));707708		let s = Scheduled {709			maybe_id: Some(id.clone()),710			priority,711			call: call.clone(),712			maybe_periodic,713			origin: origin.clone(),714			_phantom: Default::default(),715		};716717		// reserve balance for periodic execution718		// let sender =719		// 	ensure_signed(<<T as Config>::Origin as From<T::PalletsOrigin>>::from(origin).into())?;720		// let repeats = match maybe_periodic {721		// 	Some(p) => p.1,722		// 	None => 1,723		// };724		// let _ = T::CallExecutor::reserve_balance(725		// 	id.clone(),726		// 	sender,727		// 	call.as_value().unwrap().clone(),728		// 	repeats,729		// );730731		Agenda::<T>::append(when, Some(s));732		let index = Agenda::<T>::decode_len(when).unwrap_or(1) as u32 - 1;733		let address = (when, index);734		Lookup::<T>::insert(&id, &address);735		Self::deposit_event(Event::Scheduled { when, index });736737		Ok(address)738	}739740	fn do_cancel_named(origin: Option<T::PalletsOrigin>, id: ScheduledId) -> DispatchResult {741		Lookup::<T>::try_mutate_exists(id, |lookup| -> DispatchResult {742			if let Some((when, index)) = lookup.take() {743				let i = index as usize;744				Agenda::<T>::try_mutate(when, |agenda| -> DispatchResult {745					if let Some(s) = agenda.get_mut(i) {746						if let (Some(ref o), Some(ref s)) = (origin.clone(), s.borrow()) {747							if matches!(748								T::OriginPrivilegeCmp::cmp_privilege(o, &s.origin),749								Some(Ordering::Less) | None750							) {751								return Err(BadOrigin.into());752							}753							// release balance reserve754							// let sender = ensure_signed(755							// 	<<T as Config>::Origin as From<T::PalletsOrigin>>::from(756							// 		origin.unwrap(),757							// 	)758							// 	.into(),759							// )?;760							// let _ = T::CallExecutor::cancel_reserve(id, sender);761762							s.call.ensure_unrequested::<T::PreimageProvider>();763						}764						*s = None;765					}766					Ok(())767				})?;768769				Self::deposit_event(Event::Canceled { when, index });770				Ok(())771			} else {772				Err(Error::<T>::NotFound)?773			}774		})775	}776777	fn do_reschedule_named(778		id: ScheduledId,779		new_time: DispatchTime<T::BlockNumber>,780	) -> Result<TaskAddress<T::BlockNumber>, DispatchError> {781		let new_time = Self::resolve_time(new_time)?;782783		Lookup::<T>::try_mutate_exists(784			id,785			|lookup| -> Result<TaskAddress<T::BlockNumber>, DispatchError> {786				let (when, index) = lookup.ok_or(Error::<T>::NotFound)?;787788				if new_time == when {789					return Err(Error::<T>::RescheduleNoChange.into());790				}791792				Agenda::<T>::try_mutate(when, |agenda| -> DispatchResult {793					let task = agenda.get_mut(index as usize).ok_or(Error::<T>::NotFound)?;794					let task = task.take().ok_or(Error::<T>::NotFound)?;795					Agenda::<T>::append(new_time, Some(task));796797					Ok(())798				})?;799800				let new_index = Agenda::<T>::decode_len(new_time).unwrap_or(1) as u32 - 1;801				Self::deposit_event(Event::Canceled { when, index });802				Self::deposit_event(Event::Scheduled {803					when: new_time,804					index: new_index,805				});806807				*lookup = Some((new_time, new_index));808809				Ok((new_time, new_index))810			},811		)812	}813}814815impl<T: Config> schedule::v2::Anon<T::BlockNumber, <T as Config>::Call, T::PalletsOrigin>816	for Pallet<T>817{818	type Address = TaskAddress<T::BlockNumber>;819	type Hash = T::Hash;820821	fn schedule(822		when: DispatchTime<T::BlockNumber>,823		maybe_periodic: Option<schedule::Period<T::BlockNumber>>,824		priority: schedule::Priority,825		origin: T::PalletsOrigin,826		call: CallOrHashOf<T>,827	) -> Result<Self::Address, DispatchError> {828		Self::do_schedule(when, maybe_periodic, priority, origin, call)829	}830831	fn cancel((when, index): Self::Address) -> Result<(), ()> {832		Self::do_cancel(None, (when, index)).map_err(|_| ())833	}834835	fn reschedule(836		address: Self::Address,837		when: DispatchTime<T::BlockNumber>,838	) -> Result<Self::Address, DispatchError> {839		Self::do_reschedule(address, when)840	}841842	fn next_dispatch_time((when, index): Self::Address) -> Result<T::BlockNumber, ()> {843		Agenda::<T>::get(when)844			.get(index as usize)845			.ok_or(())846			.map(|_| when)847	}848}849850impl<T: Config> schedule::v2::Named<T::BlockNumber, <T as Config>::Call, T::PalletsOrigin>851	for Pallet<T>852{853	type Address = TaskAddress<T::BlockNumber>;854	type Hash = T::Hash;855856	fn schedule_named(857		id: Vec<u8>,858		when: DispatchTime<T::BlockNumber>,859		maybe_periodic: Option<schedule::Period<T::BlockNumber>>,860		priority: schedule::Priority,861		origin: T::PalletsOrigin,862		call: CallOrHashOf<T>,863	) -> Result<Self::Address, ()> {864		let inner_id: ScheduledId = id865			.try_into()866			.unwrap_or([0; MAX_TASK_ID_LENGTH_IN_BYTES as usize]);867		Self::do_schedule_named(inner_id, when, maybe_periodic, priority, origin, call)868			.map_err(|_| ())869	}870871	fn cancel_named(id: Vec<u8>) -> Result<(), ()> {872		let inner_id: ScheduledId = id873			.try_into()874			.unwrap_or([0; MAX_TASK_ID_LENGTH_IN_BYTES as usize]);875		Self::do_cancel_named(None, inner_id).map_err(|_| ())876	}877878	fn reschedule_named(879		id: Vec<u8>,880		when: DispatchTime<T::BlockNumber>,881	) -> Result<Self::Address, DispatchError> {882		let inner_id: ScheduledId = id883			.try_into()884			.unwrap_or([0; MAX_TASK_ID_LENGTH_IN_BYTES as usize]);885		Self::do_reschedule_named(inner_id, when)886	}887888	fn next_dispatch_time(id: Vec<u8>) -> Result<T::BlockNumber, ()> {889		let inner_id: ScheduledId = id890			.try_into()891			.unwrap_or([0; MAX_TASK_ID_LENGTH_IN_BYTES as usize]);892		Lookup::<T>::get(inner_id)893			.and_then(|(when, index)| Agenda::<T>::get(when).get(index as usize).map(|_| when))894			.ok_or(())895	}896}