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

difftreelog

source

pallets/scheduler/src/lib.rs23.0 KiBsourcehistory
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//! # Schedulerdo_reschedule36//!37//! This Pallet exposes capabilities for scheduling dispatches to occur at a38//! specified block number or at a specified period. These scheduled dispatches39//! may be named or anonymous and may be canceled.40//!41//! **NOTE:** The scheduled calls will be dispatched with the default filter42//! for the origin: namely `frame_system::Config::BaseCallFilter` for all origin43//! except root which will get no filter. And not the filter contained in origin44//! use to call `fn schedule`.45//!46//! If a call is scheduled using proxy or whatever mecanism which adds filter,47//! then those filter will not be used when dispatching the schedule call.48//!49//! ## Interface50//!51//! ### Dispatchable Functions52//!53//! * `schedule` - schedule a dispatch, which may be periodic, to occur at a specified block and54//!   with a specified priority.55//! * `cancel` - cancel a scheduled dispatch, specified by block number and index.56//! * `schedule_named` - augments the `schedule` interface with an additional `Vec<u8>` parameter57//!   that can be used for identification.58//! * `cancel_named` - the named complement to the cancel function.5960// Ensure we're `no_std` when compiling for Wasm.61#![cfg_attr(not(feature = "std"), no_std)]6263#[cfg(feature = "runtime-benchmarks")]64mod benchmarking;6566pub mod weights;6768use sp_core::H160;69use codec::{Codec, Decode, Encode};70use frame_system::{self as system, ensure_signed};71pub use pallet::*;72use scale_info::TypeInfo;73use sp_runtime::{74	traits::{BadOrigin, One, Saturating, Zero},75	RuntimeDebug, DispatchErrorWithPostInfo,76};77use sp_std::{borrow::Borrow, cmp::Ordering, marker::PhantomData, prelude::*};7879use frame_support::{80	dispatch::{DispatchError, DispatchResult, Dispatchable, Parameter},81	traits::{82		schedule::{self, DispatchTime, MaybeHashed},83		NamedReservableCurrency, EnsureOrigin, Get, IsType, OriginTrait, PrivilegeCmp,84		StorageVersion,85	},86	weights::{GetDispatchInfo, Weight},87};8889pub use weights::WeightInfo;9091/// Just a simple index for naming period tasks.92pub type PeriodicIndex = u32;93/// The location of a scheduled task that can be used to remove it.94pub type TaskAddress<BlockNumber> = (BlockNumber, u32);95pub const MAX_TASK_ID_LENGTH_IN_BYTES: u8 = 16;9697type ScheduledId = [u8; MAX_TASK_ID_LENGTH_IN_BYTES as usize];98pub type CallOrHashOf<T> = MaybeHashed<<T as Config>::Call, <T as frame_system::Config>::Hash>;99100/// Information regarding an item to be executed in the future.101#[cfg_attr(any(feature = "std", test), derive(PartialEq, Eq))]102#[derive(Clone, RuntimeDebug, Encode, Decode, TypeInfo)]103pub struct ScheduledV3<Call, BlockNumber, PalletsOrigin, AccountId> {104	/// The unique identity for this task, if there is one.105	maybe_id: Option<ScheduledId>,106	/// This task's priority.107	priority: schedule::Priority,108	/// The call to be dispatched.109	call: Call,110	/// If the call is periodic, then this points to the information concerning that.111	maybe_periodic: Option<schedule::Period<BlockNumber>>,112	/// The origin to dispatch the call.113	origin: PalletsOrigin,114	_phantom: PhantomData<AccountId>,115}116117pub type ScheduledV3Of<T> = ScheduledV3<118	CallOrHashOf<T>,119	<T as frame_system::Config>::BlockNumber,120	<T as Config>::PalletsOrigin,121	<T as frame_system::Config>::AccountId,122>;123124pub type ScheduledOf<T> = ScheduledV3Of<T>;125126/// The current version of Scheduled struct.127pub type Scheduled<Call, BlockNumber, PalletsOrigin, AccountId> =128	ScheduledV3<Call, BlockNumber, PalletsOrigin, AccountId>;129130#[cfg(feature = "runtime-benchmarks")]131mod preimage_provider {132	use frame_support::traits::PreimageRecipient;133	pub trait PreimageProviderAndMaybeRecipient<H>: PreimageRecipient<H> {}134	impl<H, T: PreimageRecipient<H>> PreimageProviderAndMaybeRecipient<H> for T {}135}136137#[cfg(not(feature = "runtime-benchmarks"))]138mod preimage_provider {139	use frame_support::traits::PreimageProvider;140	pub trait PreimageProviderAndMaybeRecipient<H>: PreimageProvider<H> {}141	impl<H, T: PreimageProvider<H>> PreimageProviderAndMaybeRecipient<H> for T {}142}143144pub use preimage_provider::PreimageProviderAndMaybeRecipient;145146pub(crate) trait MarginalWeightInfo: WeightInfo {147	fn item(periodic: bool, named: bool, resolved: Option<bool>) -> Weight {148		match (periodic, named, resolved) {149			(_, false, None) => Self::on_initialize_aborted(2) - Self::on_initialize_aborted(1),150			(_, true, None) => {151				Self::on_initialize_named_aborted(2) - Self::on_initialize_named_aborted(1)152			}153			(false, false, Some(false)) => Self::on_initialize(2) - Self::on_initialize(1),154			(false, true, Some(false)) => {155				Self::on_initialize_named(2) - Self::on_initialize_named(1)156			}157			(true, false, Some(false)) => {158				Self::on_initialize_periodic(2) - Self::on_initialize_periodic(1)159			}160			(true, true, Some(false)) => {161				Self::on_initialize_periodic_named_resolved(2)162					- Self::on_initialize_periodic_named_resolved(1)163			}164			(false, false, Some(true)) => Self::on_initialize(2) - Self::on_initialize(1),165			(false, true, Some(true)) => {166				Self::on_initialize_named_resolved(2) - Self::on_initialize_named_resolved(1)167			}168			(true, false, Some(true)) => {169				Self::on_initialize_periodic_resolved(2) - Self::on_initialize_periodic_resolved(1)170			}171			(true, true, Some(true)) => {172				Self::on_initialize_periodic_named_resolved(2)173					- Self::on_initialize_periodic_named_resolved(1)174			}175		}176	}177}178impl<T: WeightInfo> MarginalWeightInfo for T {}179180#[frame_support::pallet]181pub mod pallet {182	use super::*;183	use frame_support::{184		dispatch::PostDispatchInfo,185		pallet_prelude::*,186		traits::{schedule::LookupError, PreimageProvider},187	};188	use frame_system::pallet_prelude::*;189190	/// The current storage version.191	const STORAGE_VERSION: StorageVersion = StorageVersion::new(3);192193	#[pallet::pallet]194	#[pallet::generate_store(pub(super) trait Store)]195	#[pallet::storage_version(STORAGE_VERSION)]196	#[pallet::without_storage_info]197	pub struct Pallet<T>(_);198199	/// `system::Config` should always be included in our implied traits.200	#[pallet::config]201	pub trait Config: frame_system::Config {202		/// The overarching event type.203		type Event: From<Event<Self>> + IsType<<Self as frame_system::Config>::Event>;204205		/// The aggregated origin which the dispatch will take.206		type Origin: OriginTrait<PalletsOrigin = Self::PalletsOrigin>207			+ From<Self::PalletsOrigin>208			+ IsType<<Self as system::Config>::Origin>;209210		/// The caller origin, overarching type of all pallets origins.211		type PalletsOrigin: From<system::RawOrigin<Self::AccountId>> + Codec + Clone + Eq + TypeInfo;212213		type Currency: NamedReservableCurrency<Self::AccountId, ReserveIdentifier = ScheduledId>;214215		/// The aggregated call type.216		type Call: Parameter217			+ Dispatchable<Origin = <Self as Config>::Origin, PostInfo = PostDispatchInfo>218			+ GetDispatchInfo219			+ From<system::Call<Self>>;220221		/// The maximum weight that may be scheduled per block for any dispatchables of less222		/// priority than `schedule::HARD_DEADLINE`.223		#[pallet::constant]224		type MaximumWeight: Get<Weight>;225226		/// Required origin to schedule or cancel calls.227		type ScheduleOrigin: EnsureOrigin<<Self as system::Config>::Origin>;228229		/// Compare the privileges of origins.230		///231		/// This will be used when canceling a task, to ensure that the origin that tries232		/// to cancel has greater or equal privileges as the origin that created the scheduled task.233		///234		/// For simplicity the [`EqualPrivilegeOnly`](frame_support::traits::EqualPrivilegeOnly) can235		/// be used. This will only check if two given origins are equal.236		type OriginPrivilegeCmp: PrivilegeCmp<Self::PalletsOrigin>;237238		/// The maximum number of scheduled calls in the queue for a single block.239		/// Not strictly enforced, but used for weight estimation.240		#[pallet::constant]241		type MaxScheduledPerBlock: Get<u32>;242243		/// Weight information for extrinsics in this pallet.244		type WeightInfo: WeightInfo;245246		/// The preimage provider with which we look up call hashes to get the call.247		type PreimageProvider: PreimageProviderAndMaybeRecipient<Self::Hash>;248249		/// If `Some` then the number of blocks to postpone execution for when the item is delayed.250		type NoPreimagePostponement: Get<Option<Self::BlockNumber>>;251252		/// Sponsoring function.253		// type SponsorshipHandler: SponsorshipHandler<Self::AccountId, <Self as Config>::Call>;254255		/// The helper type used for custom transaction fee logic.256		type CallExecutor: DispatchCall<Self, H160>;257	}258259	/// A Scheduler-Runtime interface for finer payment handling.260	pub trait DispatchCall<T: frame_system::Config + Config, SelfContainedSignedInfo> {261		/// Reserve the maximum spendings on a call.262		fn reserve_balance(263			id: ScheduledId,264			sponsor: <T as frame_system::Config>::AccountId,265			call: <T as Config>::Call,266			count: u32,267		) -> Result<(), DispatchError>;268269		/// Pay for call dispatch (un-reserve) from the reserved funds, returning the change.270		fn pay_for_call(271			id: ScheduledId,272			sponsor: <T as frame_system::Config>::AccountId,273			call: <T as Config>::Call,274		) -> Result<u128, DispatchError>;275276		/// Resolve the call dispatch, including any post-dispatch operations.277		fn dispatch_call(278			signer: T::AccountId,279			function: <T as Config>::Call,280		) -> Result<281			Result<PostDispatchInfo, DispatchErrorWithPostInfo<PostDispatchInfo>>,282			TransactionValidityError,283		>;284285		/// Release reserved funds.286		fn cancel_reserve(287			id: ScheduledId,288			sponsor: <T as frame_system::Config>::AccountId,289		) -> Result<u128, DispatchError>;290	}291292	/// Items to be executed, indexed by the block number that they should be executed on.293	#[pallet::storage]294	pub type Agenda<T: Config> =295		StorageMap<_, Twox64Concat, T::BlockNumber, Vec<Option<ScheduledV3Of<T>>>, ValueQuery>;296297	/// Lookup from identity to the block number and index of the task.298	#[pallet::storage]299	pub(crate) type Lookup<T: Config> =300		StorageMap<_, Twox64Concat, ScheduledId, TaskAddress<T::BlockNumber>>;301302	/// Events type.303	#[pallet::event]304	#[pallet::generate_deposit(pub(super) fn deposit_event)]305	pub enum Event<T: Config> {306		/// Scheduled some task.307		Scheduled { when: T::BlockNumber, index: u32 },308		/// Canceled some task.309		Canceled { when: T::BlockNumber, index: u32 },310		/// Dispatched some task.311		Dispatched {312			task: TaskAddress<T::BlockNumber>,313			id: Option<ScheduledId>,314			result: DispatchResult,315		},316		/// The call for the provided hash was not found so the task has been aborted.317		CallLookupFailed {318			task: TaskAddress<T::BlockNumber>,319			id: Option<ScheduledId>,320			error: LookupError,321		},322	}323324	#[pallet::error]325	pub enum Error<T> {326		/// Failed to schedule a call327		FailedToSchedule,328		/// Cannot find the scheduled call.329		NotFound,330		/// Given target block number is in the past.331		TargetBlockNumberInPast,332		/// Reschedule failed because it does not change scheduled time.333		RescheduleNoChange,334	}335336	#[pallet::hooks]337	impl<T: Config> Hooks<BlockNumberFor<T>> for Pallet<T> {338		/// Execute the scheduled calls339		fn on_initialize(now: T::BlockNumber) -> Weight {340			let limit = T::MaximumWeight::get();341342			let mut queued = Agenda::<T>::take(now)343				.into_iter()344				.enumerate()345				.filter_map(|(index, s)| Some((index as u32, s?)))346				.collect::<Vec<_>>();347348			if queued.len() as u32 > T::MaxScheduledPerBlock::get() {349				log::warn!(350					target: "runtime::scheduler",351					"Warning: This block has more items queued in Scheduler than \352					expected from the runtime configuration. An update might be needed."353				);354			}355356			queued.sort_by_key(|(_, s)| s.priority);357358			let next = now + One::one();359360			let mut total_weight: Weight = T::WeightInfo::on_initialize(0);361			for (order, (index, mut s)) in queued.into_iter().enumerate() {362				let named = if let Some(ref id) = s.maybe_id {363					Lookup::<T>::remove(id);364					true365				} else {366					false367				};368369				let (call, maybe_completed) = s.call.resolved::<T::PreimageProvider>();370				s.call = call;371372				let resolved = if let Some(completed) = maybe_completed {373					T::PreimageProvider::unrequest_preimage(&completed);374					true375				} else {376					false377				};378				let call = match s.call.as_value().cloned() {379					Some(c) => c,380					None => {381						// Preimage not available - postpone until some block.382						total_weight.saturating_accrue(T::WeightInfo::item(false, named, None));383						if let Some(delay) = T::NoPreimagePostponement::get() {384							let until = now.saturating_add(delay);385							if let Some(ref id) = s.maybe_id {386								let index = Agenda::<T>::decode_len(until).unwrap_or(0);387								Lookup::<T>::insert(id, (until, index as u32));388							}389							Agenda::<T>::append(until, Some(s));390						}391						continue;392					}393				};394395				let periodic = s.maybe_periodic.is_some();396				let call_weight = call.get_dispatch_info().weight;397				let mut item_weight = T::WeightInfo::item(periodic, named, Some(resolved));398				let origin =399					<<T as Config>::Origin as From<T::PalletsOrigin>>::from(s.origin.clone())400						.into();401				if ensure_signed(origin).is_ok() {402					// Weights of Signed dispatches expect their signing account to be whitelisted.403					item_weight.saturating_accrue(T::DbWeight::get().reads_writes(1, 1));404				}405406				// We allow a scheduled call if any is true:407				// - It's priority is `HARD_DEADLINE`408				// - It does not push the weight past the limit.409				// - It is the first item in the schedule410				let hard_deadline = s.priority <= schedule::HARD_DEADLINE;411				let test_weight = total_weight412					.saturating_add(call_weight)413					.saturating_add(item_weight);414				if !hard_deadline && order > 0 && test_weight > limit {415					// Cannot be scheduled this block - postpone until next.416					total_weight.saturating_accrue(T::WeightInfo::item(false, named, None));417					if let Some(ref id) = s.maybe_id {418						// NOTE: We could reasonably not do this (in which case there would be one419						// block where the named and delayed item could not be referenced by name),420						// but we will do it anyway since it should be mostly free in terms of421						// weight and it is slightly cleaner.422						let index = Agenda::<T>::decode_len(next).unwrap_or(0);423						Lookup::<T>::insert(id, (next, index as u32));424					}425					Agenda::<T>::append(next, Some(s));426					continue;427				}428429				let sender = ensure_signed(430					<<T as Config>::Origin as From<T::PalletsOrigin>>::from(s.origin.clone())431						.into(),432				)433				.unwrap();434435				// // if call have id it was be reserved436				// if s.maybe_id.is_some() {437				// 	let _ = T::CallExecutor::pay_for_call(438				// 		s.maybe_id.unwrap(),439				// 		sender.clone(),440				// 		call.clone(),441				// 	);442				// }443444				let r = T::CallExecutor::dispatch_call(sender, call.clone());445446				let mut actual_call_weight: Weight = item_weight;447				let result: Result<_, DispatchError> = match r {448					Ok(o) => match o {449						Ok(di) => {450							actual_call_weight = di.actual_weight.unwrap_or(item_weight);451							Ok(())452						}453						Err(err) => Err(err.error),454					},455					Err(_) => {456						log::error!(457							target: "runtime::scheduler",458							"Warning: Scheduler has failed to execute a post-dispatch transaction. \459							This block might have become invalid.");460						Err(DispatchError::CannotLookup)461					} // todo possibly force a skip/return here, do something with the error462				};463464				total_weight.saturating_accrue(item_weight);465				total_weight.saturating_accrue(actual_call_weight);466467				Self::deposit_event(Event::Dispatched {468					task: (now, index),469					id: s.maybe_id.clone(),470					result,471				});472473				if let &Some((period, count)) = &s.maybe_periodic {474					if count > 1 {475						s.maybe_periodic = Some((period, count - 1));476					} else {477						s.maybe_periodic = None;478					}479					let wake = now + period;480					// If scheduled is named, place its information in `Lookup`481					if let Some(ref id) = s.maybe_id {482						let wake_index = Agenda::<T>::decode_len(wake).unwrap_or(0);483						Lookup::<T>::insert(id, (wake, wake_index as u32));484					}485					Agenda::<T>::append(wake, Some(s));486				}487			}488			0489			//total_weight490		}491	}492493	#[pallet::call]494	impl<T: Config> Pallet<T> {495		/// Schedule a named task.496		#[pallet::weight(<T as Config>::WeightInfo::schedule_named(T::MaxScheduledPerBlock::get()))]497		pub fn schedule_named(498			origin: OriginFor<T>,499			id: ScheduledId,500			when: T::BlockNumber,501			maybe_periodic: Option<schedule::Period<T::BlockNumber>>,502			priority: schedule::Priority,503			call: Box<CallOrHashOf<T>>,504		) -> DispatchResult {505			T::ScheduleOrigin::ensure_origin(origin.clone())?;506			let origin = <T as Config>::Origin::from(origin);507			Self::do_schedule_named(508				id,509				DispatchTime::At(when),510				maybe_periodic,511				priority,512				origin.caller().clone(),513				*call,514			)?;515			Ok(())516		}517518		/// Cancel a named scheduled task.519		#[pallet::weight(<T as Config>::WeightInfo::cancel_named(T::MaxScheduledPerBlock::get()))]520		pub fn cancel_named(origin: OriginFor<T>, id: ScheduledId) -> DispatchResult {521			T::ScheduleOrigin::ensure_origin(origin.clone())?;522			let origin = <T as Config>::Origin::from(origin);523			Self::do_cancel_named(Some(origin.caller().clone()), id)?;524			Ok(())525		}526527		/// Schedule a named task after a delay.528		///529		/// # <weight>530		/// Same as [`schedule_named`](Self::schedule_named).531		/// # </weight>532		#[pallet::weight(<T as Config>::WeightInfo::schedule_named(T::MaxScheduledPerBlock::get()))]533		pub fn schedule_named_after(534			origin: OriginFor<T>,535			id: ScheduledId,536			after: T::BlockNumber,537			maybe_periodic: Option<schedule::Period<T::BlockNumber>>,538			priority: schedule::Priority,539			call: Box<CallOrHashOf<T>>,540		) -> DispatchResult {541			T::ScheduleOrigin::ensure_origin(origin.clone())?;542			let origin = <T as Config>::Origin::from(origin);543			Self::do_schedule_named(544				id,545				DispatchTime::After(after),546				maybe_periodic,547				priority,548				origin.caller().clone(),549				*call,550			)?;551			Ok(())552		}553	}554}555556impl<T: Config> Pallet<T> {557	#[cfg(feature = "try-runtime")]558	pub fn pre_migrate_to_v3() -> Result<(), &'static str> {559		Ok(())560	}561562	#[cfg(feature = "try-runtime")]563	pub fn post_migrate_to_v3() -> Result<(), &'static str> {564		use frame_support::dispatch::GetStorageVersion;565566		assert!(Self::current_storage_version() == 3);567		for k in Agenda::<T>::iter_keys() {568			let _ = Agenda::<T>::try_get(k).map_err(|()| "Invalid item in Agenda")?;569		}570		Ok(())571	}572573	/// Helper to migrate scheduler when the pallet origin type has changed.574	pub fn migrate_origin<OldOrigin: Into<T::PalletsOrigin> + codec::Decode>() {575		Agenda::<T>::translate::<576			Vec<Option<Scheduled<CallOrHashOf<T>, T::BlockNumber, OldOrigin, T::AccountId>>>,577			_,578		>(|_, agenda| {579			Some(580				agenda581					.into_iter()582					.map(|schedule| {583						schedule.map(|schedule| Scheduled {584							maybe_id: schedule.maybe_id,585							priority: schedule.priority,586							call: schedule.call,587							maybe_periodic: schedule.maybe_periodic,588							origin: schedule.origin.into(),589							_phantom: Default::default(),590						})591					})592					.collect::<Vec<_>>(),593			)594		});595	}596597	fn resolve_time(when: DispatchTime<T::BlockNumber>) -> Result<T::BlockNumber, DispatchError> {598		let now = frame_system::Pallet::<T>::block_number();599600		let when = match when {601			DispatchTime::At(x) => x,602			// The current block has already completed it's scheduled tasks, so603			// Schedule the task at lest one block after this current block.604			DispatchTime::After(x) => now.saturating_add(x).saturating_add(One::one()),605		};606607		if when <= now {608			return Err(Error::<T>::TargetBlockNumberInPast.into());609		}610611		Ok(when)612	}613614	fn do_schedule_named(615		id: ScheduledId,616		when: DispatchTime<T::BlockNumber>,617		maybe_periodic: Option<schedule::Period<T::BlockNumber>>,618		priority: schedule::Priority,619		origin: T::PalletsOrigin,620		call: CallOrHashOf<T>,621	) -> Result<TaskAddress<T::BlockNumber>, DispatchError> {622		// ensure id it is unique623		if Lookup::<T>::contains_key(&id) {624			return Err(Error::<T>::FailedToSchedule)?;625		}626627		let when = Self::resolve_time(when)?;628629		call.ensure_requested::<T::PreimageProvider>();630631		// sanitize maybe_periodic632		let maybe_periodic = maybe_periodic633			.filter(|p| p.1 > 1 && !p.0.is_zero())634			// Remove one from the number of repetitions since we will schedule one now.635			.map(|(p, c)| (p, c - 1));636637		let s = Scheduled {638			maybe_id: Some(id.clone()),639			priority,640			call: call.clone(),641			maybe_periodic,642			origin: origin.clone(),643			_phantom: Default::default(),644		};645646		// reserve balance for periodic execution647		// let sender =648		// 	ensure_signed(<<T as Config>::Origin as From<T::PalletsOrigin>>::from(origin).into())?;649		// let repeats = match maybe_periodic {650		// 	Some(p) => p.1,651		// 	None => 1,652		// };653		// let _ = T::CallExecutor::reserve_balance(654		// 	id.clone(),655		// 	sender,656		// 	call.as_value().unwrap().clone(),657		// 	repeats,658		// );659660		Agenda::<T>::append(when, Some(s));661		let index = Agenda::<T>::decode_len(when).unwrap_or(1) as u32 - 1;662		let address = (when, index);663		Lookup::<T>::insert(&id, &address);664		Self::deposit_event(Event::Scheduled { when, index });665666		Ok(address)667	}668669	fn do_cancel_named(origin: Option<T::PalletsOrigin>, id: ScheduledId) -> DispatchResult {670		Lookup::<T>::try_mutate_exists(id, |lookup| -> DispatchResult {671			if let Some((when, index)) = lookup.take() {672				let i = index as usize;673				Agenda::<T>::try_mutate(when, |agenda| -> DispatchResult {674					if let Some(s) = agenda.get_mut(i) {675						if let (Some(ref o), Some(ref s)) = (origin.clone(), s.borrow()) {676							if matches!(677								T::OriginPrivilegeCmp::cmp_privilege(o, &s.origin),678								Some(Ordering::Less) | None679							) {680								return Err(BadOrigin.into());681							}682							// release balance reserve683							// let sender = ensure_signed(684							// 	<<T as Config>::Origin as From<T::PalletsOrigin>>::from(685							// 		origin.unwrap(),686							// 	)687							// 	.into(),688							// )?;689							// let _ = T::CallExecutor::cancel_reserve(id, sender);690691							s.call.ensure_unrequested::<T::PreimageProvider>();692						}693						*s = None;694					}695					Ok(())696				})?;697698				Self::deposit_event(Event::Canceled { when, index });699				Ok(())700			} else {701				Err(Error::<T>::NotFound)?702			}703		})704	}705}