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

difftreelog

source

pallets/scheduler/src/lib.rs49.8 KiBsourcehistory
1// This file is part of Substrate.23// Copyright (C) 2017-2021 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 module for scheduling dispatches.20//!21//! - [`Config`]22//! - [`Call`]23//! - [`Module`]24//!25//! ## Overview26//!27//! This module 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 a44//!   specified block and with a specified priority.45//! * `cancel` - cancel a scheduled dispatch, specified by block number and46//!   index.47//! * `schedule_named` - augments the `schedule` interface with an additional48//!   `Vec<u8>` parameter that can be used for identification.49//! * `cancel_named` - the named complement to the cancel function.5051// Ensure we're `no_std` when compiling for Wasm.52#![cfg_attr(not(feature = "std"), no_std)]5354mod benchmarking;55pub mod weights;5657use sp_std::{prelude::*, marker::PhantomData, borrow::Borrow};58use codec::{Encode, Decode, Codec};59use sp_runtime::{RuntimeDebug, traits::{Zero, One, BadOrigin, Saturating}};60use frame_support::{61	decl_module, decl_storage, decl_event, decl_error, IterableStorageMap,62	dispatch::{Dispatchable, DispatchError, DispatchResult, Parameter},63	traits::{Get, schedule::{self, DispatchTime}, OriginTrait, EnsureOrigin, IsType},64	weights::{GetDispatchInfo, Weight},65};66use frame_system::{self as system, ensure_signed};67use pallet_nft::*;68// use pallet_nft_transaction_payment::{self as nft_transaction_payment};69use nft_data_structs::*;70pub use weights::WeightInfo;7172/// Our pallet's configuration trait. All our types and constants go in here. If the73/// pallet is dependent on specific other pallets, then their configuration traits74/// should be added to our implied traits list.75///76/// `system::Config` should always be included in our implied traits.77/// // 78pub trait Config: system::Config79{ 8081	/// The overarching event type.82	type Event: From<Event<Self>> + Into<<Self as system::Config>::Event>;8384	/// The aggregated origin which the dispatch will take.85	type Origin: OriginTrait<PalletsOrigin = Self::PalletsOrigin>86		+ From<Self::PalletsOrigin> + IsType<<Self as system::Config>::Origin>;8788	/// The caller origin, overarching type of all pallets origins.89	type PalletsOrigin: From<system::RawOrigin<Self::AccountId>> + Codec + Clone + Eq;9091	/// The aggregated call type.92	type Call: Parameter + Dispatchable<Origin=<Self as Config>::Origin> + GetDispatchInfo + From<system::Call<Self>>;9394	/// The maximum weight that may be scheduled per block for any dispatchables of less priority95	/// than `schedule::HARD_DEADLINE`.96	type MaximumWeight: Get<Weight>;9798	/// Required origin to schedule or cancel calls.99	type ScheduleOrigin: EnsureOrigin<<Self as system::Config>::Origin>;100101	/// The maximum number of scheduled calls in the queue for a single block.102	/// Not strictly enforced, but used for weight estimation.103	type MaxScheduledPerBlock: Get<u32>;104105	/// TODO106	type Sponsoring: SponsoringResolve<Self::AccountId, <Self as Config>::Call>;107108	/// Weight information for extrinsics in this pallet.109	type WeightInfo: WeightInfo;110}111112// pub type SelfWeightInfo<T> = <T as system::Config>::WeightInfo;113114/// Just a simple index for naming period tasks.115pub type PeriodicIndex = u32;116/// The location of a scheduled task that can be used to remove it.117pub type TaskAddress<BlockNumber> = (BlockNumber, u32);118119#[cfg_attr(any(feature = "std", test), derive(PartialEq, Eq))]120#[derive(Clone, RuntimeDebug, Encode, Decode)]121struct ScheduledV1<Call, BlockNumber> {122	maybe_id: Option<Vec<u8>>,123	priority: schedule::Priority,124	call: Call,125	maybe_periodic: Option<schedule::Period<BlockNumber>>,126}127128/// Information regarding an item to be executed in the future.129#[cfg_attr(any(feature = "std", test), derive(PartialEq, Eq))]130#[derive(Clone, RuntimeDebug, Encode, Decode)]131pub struct ScheduledV2<Call, BlockNumber, PalletsOrigin, AccountId> {132	/// The unique identity for this task, if there is one.133	maybe_id: Option<Vec<u8>>,134	/// This task's priority.135	priority: schedule::Priority,136	/// The call to be dispatched.137	call: Call,138	/// If the call is periodic, then this points to the information concerning that.139	maybe_periodic: Option<schedule::Period<BlockNumber>>,140	/// The origin to dispatch the call.141	origin: PalletsOrigin,142	_phantom: PhantomData<AccountId>,143}144145/// The current version of Scheduled struct.146pub type Scheduled<Call, BlockNumber, PalletsOrigin, AccountId> = ScheduledV2<Call, BlockNumber, PalletsOrigin, AccountId>;147148// A value placed in storage that represents the current version of the Scheduler storage.149// This value is used by the `on_runtime_upgrade` logic to determine whether we run150// storage migration logic.151#[derive(Encode, Decode, Clone, Copy, PartialEq, Eq, RuntimeDebug)]152enum Releases {153	V1,154	V2,155}156157impl Default for Releases {158	fn default() -> Self {159		Releases::V1160	}161}162163#[derive(Encode, Decode, Clone, PartialEq, Eq, RuntimeDebug)]164pub struct CallSpec {165166	module: u32,167	method: u32,168}169170decl_storage! {171	trait Store for Module<T: Config> as Scheduler {172		/// Items to be executed, indexed by the block number that they should be executed on.173		pub Agenda: map hasher(twox_64_concat) T::BlockNumber174			=> Vec<Option<Scheduled<<T as Config>::Call, T::BlockNumber, T::PalletsOrigin, T::AccountId>>>;175176		pub SpecAgenda: map hasher(twox_64_concat) T::BlockNumber177			=> Vec<Option<CallSpec>>;			178179		/// Lookup from identity to the block number and index of the task.180		Lookup: map hasher(twox_64_concat) Vec<u8> => Option<TaskAddress<T::BlockNumber>>;181182		/// Storage version of the pallet.183		///184		/// New networks start with last version.185		StorageVersion build(|_| Releases::V2): Releases;186	}187}188189decl_event!(190	pub enum Event<T> where <T as system::Config>::BlockNumber {191		/// Scheduled some task. \[when, index\]192		Scheduled(BlockNumber, u32),193		/// Canceled some task. \[when, index\]194		Canceled(BlockNumber, u32),195		/// Dispatched some task. \[task, id, result\]196		Dispatched(TaskAddress<BlockNumber>, Option<Vec<u8>>, DispatchResult),197	}198);199200decl_error! {201	pub enum Error for Module<T: Config> {202		/// Failed to schedule a call203		FailedToSchedule,204		/// Cannot find the scheduled call.205		NotFound,206		/// Given target block number is in the past.207		TargetBlockNumberInPast,208		/// Reschedule failed because it does not change scheduled time.209		RescheduleNoChange,210	}211}212213decl_module! {214	/// Scheduler module declaration.215	pub struct Module<T: Config> for enum Call 216	where 217		origin: <T as system::Config>::Origin218	{219		type Error = Error<T>;220		fn deposit_event() = default;221222223		/// Anonymously schedule a task.224		///225		/// # <weight>226		/// - S = Number of already scheduled calls227		/// - Base Weight: 22.29 + .126 * S µs228		/// - DB Weight:229		///     - Read: Agenda230		///     - Write: Agenda231		/// - Will use base weight of 25 which should be good for up to 30 scheduled calls232		/// # </weight>233		// #[weight = <T as Config>::WeightInfo::schedule(T::MaxScheduledPerBlock::get())]234		#[weight = 0]235		fn schedule(origin,236			when: T::BlockNumber,237			maybe_periodic: Option<schedule::Period<T::BlockNumber>>,238			priority: schedule::Priority,239			call: Box<<T as Config>::Call>,240		) 241		{242			let origin = <T as Config>::Origin::from(origin);243			Self::do_schedule(DispatchTime::At(when), maybe_periodic, priority, origin.caller().clone(), *call)?;244		}245246		/// Cancel an anonymously scheduled task.247		///248		/// # <weight>249		/// - S = Number of already scheduled calls250		/// - Base Weight: 22.15 + 2.869 * S µs251		/// - DB Weight:252		///     - Read: Agenda253		///     - Write: Agenda, Lookup254		/// - Will use base weight of 100 which should be good for up to 30 scheduled calls255		/// # </weight>256		#[weight = <T as Config>::WeightInfo::cancel(T::MaxScheduledPerBlock::get())]257		fn cancel(origin, when: T::BlockNumber, index: u32) {258			T::ScheduleOrigin::ensure_origin(origin.clone())?;259			let origin = <T as Config>::Origin::from(origin);260			Self::do_cancel(Some(origin.caller().clone()), (when, index))?;261		}262263		/// Schedule a named task.264		///265		/// # <weight>266		/// - S = Number of already scheduled calls267		/// - Base Weight: 29.6 + .159 * S µs268		/// - DB Weight:269		///     - Read: Agenda, Lookup270		///     - Write: Agenda, Lookup271		/// - Will use base weight of 35 which should be good for more than 30 scheduled calls272		/// # </weight>273		#[weight = <T as Config>::WeightInfo::schedule_named(T::MaxScheduledPerBlock::get())]274		fn schedule_named(origin,275			id: Vec<u8>,276			when: T::BlockNumber,277			maybe_periodic: Option<schedule::Period<T::BlockNumber>>,278			priority: schedule::Priority,279			call: Box<<T as Config>::Call>,280		) {281			T::ScheduleOrigin::ensure_origin(origin.clone())?;282			let origin = <T as Config>::Origin::from(origin);283			Self::do_schedule_named(284				id, DispatchTime::At(when), maybe_periodic, priority, origin.caller().clone(), *call285			)?;286		}287288		/// Cancel a named scheduled task.289		///290		/// # <weight>291		/// - S = Number of already scheduled calls292		/// - Base Weight: 24.91 + 2.907 * S µs293		/// - DB Weight:294		///     - Read: Agenda, Lookup295		///     - Write: Agenda, Lookup296		/// - Will use base weight of 100 which should be good for up to 30 scheduled calls297		/// # </weight>298		#[weight = <T as Config>::WeightInfo::cancel_named(T::MaxScheduledPerBlock::get())]299		fn cancel_named(origin, id: Vec<u8>) {300			T::ScheduleOrigin::ensure_origin(origin.clone())?;301			let origin = <T as Config>::Origin::from(origin);302			Self::do_cancel_named(Some(origin.caller().clone()), id)?;303		}304305		/// Anonymously schedule a task after a delay.306		///307		/// # <weight>308		/// Same as [`schedule`].309		/// # </weight>310		#[weight = <T as Config>::WeightInfo::schedule(T::MaxScheduledPerBlock::get())]311		fn schedule_after(origin,312			after: T::BlockNumber,313			maybe_periodic: Option<schedule::Period<T::BlockNumber>>,314			priority: schedule::Priority,315			call: Box<<T as Config>::Call>,316		) {317			T::ScheduleOrigin::ensure_origin(origin.clone())?;318			let origin = <T as Config>::Origin::from(origin);319			Self::do_schedule(320				DispatchTime::After(after), maybe_periodic, priority, origin.caller().clone(), *call321			)?;322		}323324		/// Schedule a named task after a delay.325		///326		/// # <weight>327		/// Same as [`schedule_named`].328		/// # </weight>329		#[weight = <T as Config>::WeightInfo::schedule_named(T::MaxScheduledPerBlock::get())]330		fn schedule_named_after(origin,331			id: Vec<u8>,332			after: T::BlockNumber,333			maybe_periodic: Option<schedule::Period<T::BlockNumber>>,334			priority: schedule::Priority,335			call: Box<<T as Config>::Call>,336		) {337			T::ScheduleOrigin::ensure_origin(origin.clone())?;338			let origin = <T as Config>::Origin::from(origin);339			Self::do_schedule_named(340				id, DispatchTime::After(after), maybe_periodic, priority, origin.caller().clone(), *call341			)?;342		}343344		/// Execute the scheduled calls345		///346		/// # <weight>347		/// - S = Number of already scheduled calls348		/// - N = Named scheduled calls349		/// - P = Periodic Calls350		/// - Base Weight: 9.243 + 23.45 * S µs351		/// - DB Weight:352		///     - Read: Agenda + Lookup * N + Agenda(Future) * P353		///     - Write: Agenda + Lookup * N  + Agenda(future) * P354		/// # </weight>355		fn on_initialize(now: T::BlockNumber) -> Weight {356			let limit = T::MaximumWeight::get();357			let mut queued = Agenda::<T>::take(now).into_iter()358				.enumerate()359				.filter_map(|(index, s)| s.map(|inner| (index as u32, inner)))360				.collect::<Vec<_>>();361			if queued.len() as u32 > T::MaxScheduledPerBlock::get() {362				log::warn!(363					target: "runtime::scheduler",364					"Warning: This block has more items queued in Scheduler than \365					expected from the runtime configuration. An update might be needed."366				);367			}368			queued.sort_by_key(|(_, s)| s.priority);369			let base_weight: Weight = T::DbWeight::get().reads_writes(1, 2); // Agenda + Agenda(next)370			let mut total_weight: Weight = <T as Config>::WeightInfo::schedule(T::MaxScheduledPerBlock::get());371			queued.into_iter()372				.enumerate()373				.scan(base_weight, |cumulative_weight, (order, (index, s))| {374					*cumulative_weight = cumulative_weight375						.saturating_add(s.call.get_dispatch_info().weight);376377					let origin = <<T as Config>::Origin as From<T::PalletsOrigin>>::from(378						s.origin.clone()379					).into();380381					if ensure_signed(origin).is_ok() {382						 // AccountData for inner call origin accountdata.383						*cumulative_weight = cumulative_weight384							.saturating_add(T::DbWeight::get().reads_writes(1, 1));385					}386387					if s.maybe_id.is_some() {388						// Remove/Modify Lookup389						*cumulative_weight = cumulative_weight.saturating_add(T::DbWeight::get().writes(1));390					}391					if s.maybe_periodic.is_some() {392						// Read/Write Agenda for future block393						*cumulative_weight = cumulative_weight.saturating_add(T::DbWeight::get().reads_writes(1, 1));394					}395396					Some((order, index, *cumulative_weight, s))397				})398				.filter_map(|(order, index, cumulative_weight, mut s)| {399					// We allow a scheduled call if any is true:400					// - It's priority is `HARD_DEADLINE`401					// - It does not push the weight past the limit.402					// - It is the first item in the schedule403					if s.priority <= schedule::HARD_DEADLINE || cumulative_weight <= limit || order == 0 {404405						let origin = <<T as Config>::Origin as From<T::PalletsOrigin>>::from(406							s.origin.clone()407						).into();408						let sender = ensure_signed(origin).unwrap_or(T::AccountId::default());409						let who_will_pay = T::Sponsoring::resolve(&sender, &s.call.clone()).unwrap_or(410							T::AccountId::default());411						let sponsor = T::PalletsOrigin::from(system::RawOrigin::Signed(who_will_pay));412						let r = s.call.clone().dispatch(sponsor.into());413						let maybe_id = s.maybe_id.clone();414						if let &Some((period, count)) = &s.maybe_periodic {415							if count > 1 {416								s.maybe_periodic = Some((period, count - 1));417							} else {418								s.maybe_periodic = None;419							}420							let next = now + period;421							// If scheduled is named, place it's information in `Lookup`422							if let Some(ref id) = s.maybe_id {423								let next_index = Agenda::<T>::decode_len(now + period).unwrap_or(0);424								Lookup::<T>::insert(id, (next, next_index as u32));425							}426							Agenda::<T>::append(next, Some(s));427						} else {428							if let Some(ref id) = s.maybe_id {429								Lookup::<T>::remove(id);430							}431						}432						Self::deposit_event(RawEvent::Dispatched(433							(now, index),434							maybe_id,435							r.map(|_| ()).map_err(|e| e.error)436						));437						total_weight = cumulative_weight;438						None439					} else {440						Some(Some(s))441					}442				})443				.for_each(|unused| {444					let next = now + One::one();445					Agenda::<T>::append(next, unused);446				});447448			total_weight449		}450	}451}452453impl<T: Config> Module<T> {454	/// Migrate storage format from V1 to V2.455	/// Return true if migration is performed.456	pub fn migrate_v1_to_t2() -> bool {457		if StorageVersion::get() == Releases::V1 {458			StorageVersion::put(Releases::V2);459460			Agenda::<T>::translate::<461				Vec<Option<ScheduledV1<<T as Config>::Call, T::BlockNumber>>>, _462			>(|_, agenda| Some(463				agenda464					.into_iter()465					.map(|schedule| schedule.map(|schedule| ScheduledV2 {466						maybe_id: schedule.maybe_id,467						priority: schedule.priority,468						call: schedule.call,469						maybe_periodic: schedule.maybe_periodic,470						origin: system::RawOrigin::Root.into(),471						_phantom: Default::default(),472					}))473					.collect::<Vec<_>>()474			));475476			true477		} else {478			false479		}480	}481482	/// Helper to migrate scheduler when the pallet origin type has changed.483	pub fn migrate_origin<OldOrigin: Into<T::PalletsOrigin> + codec::Decode>() {484		Agenda::<T>::translate::<485			Vec<Option<Scheduled<<T as Config>::Call, T::BlockNumber, OldOrigin, T::AccountId>>>, _486		>(|_, agenda| Some(487			agenda488				.into_iter()489				.map(|schedule| schedule.map(|schedule| Scheduled {490					maybe_id: schedule.maybe_id,491					priority: schedule.priority,492					call: schedule.call,493					maybe_periodic: schedule.maybe_periodic,494					origin: schedule.origin.into(),495					_phantom: Default::default(),496				}))497				.collect::<Vec<_>>()498		));499	}500501	fn resolve_time(when: DispatchTime<T::BlockNumber>) -> Result<T::BlockNumber, DispatchError> {502		let now = frame_system::Pallet::<T>::block_number();503504		let when = match when {505			DispatchTime::At(x) => x,506			// The current block has already completed it's scheduled tasks, so507			// Schedule the task at lest one block after this current block.508			DispatchTime::After(x) => now.saturating_add(x).saturating_add(One::one())509		};510511		if when <= now {512			return Err(Error::<T>::TargetBlockNumberInPast.into())513		}514515		Ok(when)516	}517518	fn do_schedule(519		when: DispatchTime<T::BlockNumber>,520		maybe_periodic: Option<schedule::Period<T::BlockNumber>>,521		priority: schedule::Priority,522		origin: T::PalletsOrigin,523		call: <T as Config>::Call524	) -> Result<TaskAddress<T::BlockNumber>, DispatchError> {525		let when = Self::resolve_time(when)?;526527		// sanitize maybe_periodic528		let maybe_periodic = maybe_periodic529			.filter(|p| p.1 > 1 && !p.0.is_zero())530			// Remove one from the number of repetitions since we will schedule one now.531			.map(|(p, c)| (p, c - 1));532		let s = Some(Scheduled {533			maybe_id: None, priority, call, maybe_periodic, origin, _phantom: PhantomData::<T::AccountId>::default(),534		});535		Agenda::<T>::append(when, s);536		let index = Agenda::<T>::decode_len(when).unwrap_or(1) as u32 - 1;537		if index > T::MaxScheduledPerBlock::get() {538			log::warn!(539				target: "runtime::scheduler",540				"Warning: There are more items queued in the Scheduler than \541				expected from the runtime configuration. An update might be needed.",542			);543		}544		Self::deposit_event(RawEvent::Scheduled(when, index));545546		Ok((when, index))547	}548549	fn do_cancel(550		origin: Option<T::PalletsOrigin>,551		(when, index): TaskAddress<T::BlockNumber>552	) -> Result<(), DispatchError> {553		let scheduled = Agenda::<T>::try_mutate(554			when,555			|agenda| {556				agenda.get_mut(index as usize)557					.map_or(Ok(None), |s| -> Result<Option<Scheduled<_, _, _, _>>, DispatchError> {558						if let (Some(ref o), Some(ref s)) = (origin, s.borrow()) {559							if *o != s.origin {560								return Err(BadOrigin.into());561							}562						};563						Ok(s.take())564					})565			},566		)?;567		if let Some(s) = scheduled {568			if let Some(id) = s.maybe_id {569				Lookup::<T>::remove(id);570			}571			Self::deposit_event(RawEvent::Canceled(when, index));572			Ok(())573		} else {574			Err(Error::<T>::NotFound)?575		}576	}577578	fn do_reschedule(579		(when, index): TaskAddress<T::BlockNumber>,580		new_time: DispatchTime<T::BlockNumber>,581	) -> Result<TaskAddress<T::BlockNumber>, DispatchError> {582		let new_time = Self::resolve_time(new_time)?;583584		if new_time == when {585			return Err(Error::<T>::RescheduleNoChange.into());586		}587588		Agenda::<T>::try_mutate(when, |agenda| -> DispatchResult {589			let task = agenda.get_mut(index as usize).ok_or(Error::<T>::NotFound)?;590			let task = task.take().ok_or(Error::<T>::NotFound)?;591			Agenda::<T>::append(new_time, Some(task));592			Ok(())593		})?;594595		let new_index = Agenda::<T>::decode_len(new_time).unwrap_or(1) as u32 - 1;596		Self::deposit_event(RawEvent::Canceled(when, index));597		Self::deposit_event(RawEvent::Scheduled(new_time, new_index));598599		Ok((new_time, new_index))600	}601602	fn do_schedule_named(603		id: Vec<u8>,604		when: DispatchTime<T::BlockNumber>,605		maybe_periodic: Option<schedule::Period<T::BlockNumber>>,606		priority: schedule::Priority,607		origin: T::PalletsOrigin,608		call: <T as Config>::Call,609	) -> Result<TaskAddress<T::BlockNumber>, DispatchError> {610		// ensure id it is unique611		if Lookup::<T>::contains_key(&id) {612			return Err(Error::<T>::FailedToSchedule)?613		}614615		let when = Self::resolve_time(when)?;616617		// sanitize maybe_periodic618		let maybe_periodic = maybe_periodic619			.filter(|p| p.1 > 1 && !p.0.is_zero())620			// Remove one from the number of repetitions since we will schedule one now.621			.map(|(p, c)| (p, c - 1));622623		let s = Scheduled {624			maybe_id: Some(id.clone()), priority, call, maybe_periodic, origin, _phantom: Default::default()625		};626		Agenda::<T>::append(when, Some(s));627		let index = Agenda::<T>::decode_len(when).unwrap_or(1) as u32 - 1;628		if index > T::MaxScheduledPerBlock::get() {629			log::warn!(630				target: "runtime::scheduler",631				"Warning: There are more items queued in the Scheduler than \632				expected from the runtime configuration. An update might be needed.",633			);634		}635		let address = (when, index);636		Lookup::<T>::insert(&id, &address);637		Self::deposit_event(RawEvent::Scheduled(when, index));638639		Ok(address)640	}641642	fn do_cancel_named(origin: Option<T::PalletsOrigin>, id: Vec<u8>) -> DispatchResult {643		Lookup::<T>::try_mutate_exists(id, |lookup| -> DispatchResult {644			if let Some((when, index)) = lookup.take() {645				let i = index as usize;646				Agenda::<T>::try_mutate(when, |agenda| -> DispatchResult {647					if let Some(s) = agenda.get_mut(i) {648						if let (Some(ref o), Some(ref s)) = (origin, s.borrow()) {649							if *o != s.origin {650								return Err(BadOrigin.into());651							}652						}653						*s = None;654					}655					Ok(())656				})?;657				Self::deposit_event(RawEvent::Canceled(when, index));658				Ok(())659			} else {660				Err(Error::<T>::NotFound)?661			}662		})663	}664665	fn do_reschedule_named(666		id: Vec<u8>,667		new_time: DispatchTime<T::BlockNumber>,668	) -> Result<TaskAddress<T::BlockNumber>, DispatchError> {669		let new_time = Self::resolve_time(new_time)?;670671		Lookup::<T>::try_mutate_exists(id, |lookup| -> Result<TaskAddress<T::BlockNumber>, DispatchError> {672			let (when, index) = lookup.ok_or(Error::<T>::NotFound)?;673674			if new_time == when {675				return Err(Error::<T>::RescheduleNoChange.into());676			}677678			Agenda::<T>::try_mutate(when, |agenda| -> DispatchResult {679				let task = agenda.get_mut(index as usize).ok_or(Error::<T>::NotFound)?;680				let task = task.take().ok_or(Error::<T>::NotFound)?;681				Agenda::<T>::append(new_time, Some(task));682683				Ok(())684			})?;685686			let new_index = Agenda::<T>::decode_len(new_time).unwrap_or(1) as u32 - 1;687			Self::deposit_event(RawEvent::Canceled(when, index));688			Self::deposit_event(RawEvent::Scheduled(new_time, new_index));689690			*lookup = Some((new_time, new_index));691692			Ok((new_time, new_index))693		})694	}695}696697impl<T: Config> schedule::Anon<T::BlockNumber, <T as Config>::Call, T::PalletsOrigin> for Module<T> {698	type Address = TaskAddress<T::BlockNumber>;699700	fn schedule(701		when: DispatchTime<T::BlockNumber>,702		maybe_periodic: Option<schedule::Period<T::BlockNumber>>,703		priority: schedule::Priority,704		origin: T::PalletsOrigin,705		call: <T as Config>::Call706	) -> Result<Self::Address, DispatchError> {707		Self::do_schedule(when, maybe_periodic, priority, origin, call)708	}709710	fn cancel((when, index): Self::Address) -> Result<(), ()> {711		Self::do_cancel(None, (when, index)).map_err(|_| ())712	}713714	fn reschedule(715		address: Self::Address,716		when: DispatchTime<T::BlockNumber>,717	) -> Result<Self::Address, DispatchError> {718		Self::do_reschedule(address, when)719	}720721	fn next_dispatch_time((when, index): Self::Address) -> Result<T::BlockNumber, ()> {722		Agenda::<T>::get(when).get(index as usize).ok_or(()).map(|_| when)723	}724}725726impl<T: Config> schedule::Named<T::BlockNumber, <T as Config>::Call, T::PalletsOrigin> for Module<T> {727	type Address = TaskAddress<T::BlockNumber>;728729	fn schedule_named(730		id: Vec<u8>,731		when: DispatchTime<T::BlockNumber>,732		maybe_periodic: Option<schedule::Period<T::BlockNumber>>,733		priority: schedule::Priority,734		origin: T::PalletsOrigin,735		call: <T as Config>::Call,736	) -> Result<Self::Address, ()> {737		Self::do_schedule_named(id, when, maybe_periodic, priority, origin, call).map_err(|_| ())738	}739740	fn cancel_named(id: Vec<u8>) -> Result<(), ()> {741		Self::do_cancel_named(None, id).map_err(|_| ())742	}743744	fn reschedule_named(745		id: Vec<u8>,746		when: DispatchTime<T::BlockNumber>,747	) -> Result<Self::Address, DispatchError> {748		Self::do_reschedule_named(id, when)749	}750751	fn next_dispatch_time(id: Vec<u8>) -> Result<T::BlockNumber, ()> {752		Lookup::<T>::get(id).and_then(|(when, index)| Agenda::<T>::get(when).get(index as usize).map(|_| when)).ok_or(())753	}754}755756#[cfg(test)]757mod tests {758	use super::*;759760	use frame_support::{761		parameter_types, assert_ok, ord_parameter_types,762		assert_noop, assert_err, Hashable,763		traits::{OnInitialize, OnFinalize, Filter},764		weights::constants::RocksDbWeight,765	};766	use sp_core::H256;767	use sp_runtime::{768		Perbill,769		testing::Header,770		traits::{BlakeTwo256, IdentityLookup},771	};772	use frame_system::{EnsureOneOf, EnsureRoot, EnsureSignedBy};773	use substrate_test_utils::assert_eq_uvec;774	use crate as scheduler;775776	mod logger {777		use super::*;778		use std::cell::RefCell;779780		thread_local! {781			static LOG: RefCell<Vec<(OriginCaller, u32)>> = RefCell::new(Vec::new());782		}783		pub fn log() -> Vec<(OriginCaller, u32)> {784			LOG.with(|log| log.borrow().clone())785		}786		pub trait Config: system::Config {787			type Event: From<Event> + Into<<Self as system::Config>::Event>;788		}789		decl_event! {790			pub enum Event {791				Logged(u32, Weight),792			}793		}794		decl_module! {795			pub struct Module<T: Config> for enum Call796			where797				origin: <T as system::Config>::Origin,798				<T as system::Config>::Origin: OriginTrait<PalletsOrigin = OriginCaller>799			{800				fn deposit_event() = default;801802				#[weight = *weight]803				fn log(origin, i: u32, weight: Weight) {804					Self::deposit_event(Event::Logged(i, weight));805					LOG.with(|log| {806						log.borrow_mut().push((origin.caller().clone(), i));807					})808				}809810				#[weight = *weight]811				fn log_without_filter(origin, i: u32, weight: Weight) {812					Self::deposit_event(Event::Logged(i, weight));813					LOG.with(|log| {814						log.borrow_mut().push((origin.caller().clone(), i));815					})816				}817			}818		}819	}820821	type UncheckedExtrinsic = frame_system::mocking::MockUncheckedExtrinsic<Test>;822	type Block = frame_system::mocking::MockBlock<Test>;823824	frame_support::construct_runtime!(825		pub enum Test where826			Block = Block,827			NodeBlock = Block,828			UncheckedExtrinsic = UncheckedExtrinsic,829		{830			System: frame_system::{Pallet, Call, Config, Storage, Event<T>},831			Logger: logger::{Pallet, Call, Event},832			Scheduler: scheduler::{Pallet, Call, Storage, Event<T>},833		}834	);835836	// Scheduler must dispatch with root and no filter, this tests base filter is indeed not used.837	pub struct BaseFilter;838	impl Filter<Call> for BaseFilter {839		fn filter(call: &Call) -> bool {840			!matches!(call, Call::Logger(logger::Call::log(_, _)))841		}842	}843844	parameter_types! {845		pub const BlockHashCount: u64 = 250;846		pub BlockWeights: frame_system::limits::BlockWeights =847			frame_system::limits::BlockWeights::simple_max(2_000_000_000_000);848	}849	impl system::Config for Test {850		type BaseCallFilter = BaseFilter;851		type BlockWeights = ();852		type BlockLength = ();853		type DbWeight = RocksDbWeight;854		type Origin = Origin;855		type Call = Call;856		type Index = u64;857		type BlockNumber = u64;858		type Hash = H256;859		type Hashing = BlakeTwo256;860		type AccountId = u64;861		type Lookup = IdentityLookup<Self::AccountId>;862		type Header = Header;863		type Event = Event;864		type BlockHashCount = BlockHashCount;865		type Version = ();866		type PalletInfo = PalletInfo;867		type AccountData = ();868		type OnNewAccount = ();869		type OnKilledAccount = ();870		type SystemWeightInfo = ();871		type SS58Prefix = ();872		type OnSetCode = ();873	}874	impl logger::Config for Test {875		type Event = Event;876	}877	parameter_types! {878		pub MaximumSchedulerWeight: Weight = Perbill::from_percent(80) * BlockWeights::get().max_block;879		pub const MaxScheduledPerBlock: u32 = 10;880	}881	ord_parameter_types! {882		pub const One: u64 = 1;883	}884885	impl Config for Test {886		type Event = Event;887		type Origin = Origin;888		type PalletsOrigin = OriginCaller;889		type Call = Call;890		type MaximumWeight = MaximumSchedulerWeight;891		type ScheduleOrigin = EnsureOneOf<u64, EnsureRoot<u64>, EnsureSignedBy<One, u64>>;892		type MaxScheduledPerBlock = MaxScheduledPerBlock;893		type WeightInfo = ();894	}895896	pub fn new_test_ext() -> sp_io::TestExternalities {897		let t = system::GenesisConfig::default().build_storage::<Test>().unwrap();898		t.into()899	}900901	fn run_to_block(n: u64) {902		while System::block_number() < n {903			Scheduler::on_finalize(System::block_number());904			System::set_block_number(System::block_number() + 1);905			Scheduler::on_initialize(System::block_number());906		}907	}908909	fn root() -> OriginCaller {910		system::RawOrigin::Root.into()911	}912913	#[test]914	fn basic_scheduling_works() {915		new_test_ext().execute_with(|| {916			let call = Call::Logger(logger::Call::log(42, 1000));917			assert!(!<Test as frame_system::Config>::BaseCallFilter::filter(&call));918			assert_ok!(Scheduler::do_schedule(DispatchTime::At(4), None, 127, root(), call));919			run_to_block(3);920			assert!(logger::log().is_empty());921			run_to_block(4);922			assert_eq!(logger::log(), vec![(root(), 42u32)]);923			run_to_block(100);924			assert_eq!(logger::log(), vec![(root(), 42u32)]);925		});926	}927928	#[test]929	fn schedule_after_works() {930		new_test_ext().execute_with(|| {931			run_to_block(2);932			let call = Call::Logger(logger::Call::log(42, 1000));933			assert!(!<Test as frame_system::Config>::BaseCallFilter::filter(&call));934			// This will schedule the call 3 blocks after the next block... so block 3 + 3 = 6935			assert_ok!(Scheduler::do_schedule(DispatchTime::After(3), None, 127, root(), call));936			run_to_block(5);937			assert!(logger::log().is_empty());938			run_to_block(6);939			assert_eq!(logger::log(), vec![(root(), 42u32)]);940			run_to_block(100);941			assert_eq!(logger::log(), vec![(root(), 42u32)]);942		});943	}944945	#[test]946	fn schedule_after_zero_works() {947		new_test_ext().execute_with(|| {948			run_to_block(2);949			let call = Call::Logger(logger::Call::log(42, 1000));950			assert!(!<Test as frame_system::Config>::BaseCallFilter::filter(&call));951			assert_ok!(Scheduler::do_schedule(DispatchTime::After(0), None, 127, root(), call));952			// Will trigger on the next block.953			run_to_block(3);954			assert_eq!(logger::log(), vec![(root(), 42u32)]);955			run_to_block(100);956			assert_eq!(logger::log(), vec![(root(), 42u32)]);957		});958	}959960	#[test]961	fn periodic_scheduling_works() {962		new_test_ext().execute_with(|| {963			// at #4, every 3 blocks, 3 times.964			assert_ok!(Scheduler::do_schedule(965				DispatchTime::At(4), Some((3, 3)), 127, root(), Call::Logger(logger::Call::log(42, 1000))966			));967			run_to_block(3);968			assert!(logger::log().is_empty());969			run_to_block(4);970			assert_eq!(logger::log(), vec![(root(), 42u32)]);971			run_to_block(6);972			assert_eq!(logger::log(), vec![(root(), 42u32)]);973			run_to_block(7);974			assert_eq!(logger::log(), vec![(root(), 42u32), (root(), 42u32)]);975			run_to_block(9);976			assert_eq!(logger::log(), vec![(root(), 42u32), (root(), 42u32)]);977			run_to_block(10);978			assert_eq!(logger::log(), vec![(root(), 42u32), (root(), 42u32), (root(), 42u32)]);979			run_to_block(100);980			assert_eq!(logger::log(), vec![(root(), 42u32), (root(), 42u32), (root(), 42u32)]);981		});982	}983984	#[test]985	fn reschedule_works() {986		new_test_ext().execute_with(|| {987			let call = Call::Logger(logger::Call::log(42, 1000));988			assert!(!<Test as frame_system::Config>::BaseCallFilter::filter(&call));989			assert_eq!(Scheduler::do_schedule(DispatchTime::At(4), None, 127, root(), call).unwrap(), (4, 0));990991			run_to_block(3);992			assert!(logger::log().is_empty());993994			assert_eq!(Scheduler::do_reschedule((4, 0), DispatchTime::At(6)).unwrap(), (6, 0));995996			assert_noop!(Scheduler::do_reschedule((6, 0), DispatchTime::At(6)), Error::<Test>::RescheduleNoChange);997998			run_to_block(4);999			assert!(logger::log().is_empty());10001001			run_to_block(6);1002			assert_eq!(logger::log(), vec![(root(), 42u32)]);10031004			run_to_block(100);1005			assert_eq!(logger::log(), vec![(root(), 42u32)]);1006		});1007	}10081009	#[test]1010	fn reschedule_named_works() {1011		new_test_ext().execute_with(|| {1012			let call = Call::Logger(logger::Call::log(42, 1000));1013			assert!(!<Test as frame_system::Config>::BaseCallFilter::filter(&call));1014			assert_eq!(Scheduler::do_schedule_named(1015				1u32.encode(), DispatchTime::At(4), None, 127, root(), call1016			).unwrap(), (4, 0));10171018			run_to_block(3);1019			assert!(logger::log().is_empty());10201021			assert_eq!(Scheduler::do_reschedule_named(1u32.encode(), DispatchTime::At(6)).unwrap(), (6, 0));10221023			assert_noop!(Scheduler::do_reschedule_named(1u32.encode(), DispatchTime::At(6)), Error::<Test>::RescheduleNoChange);10241025			run_to_block(4);1026			assert!(logger::log().is_empty());10271028			run_to_block(6);1029			assert_eq!(logger::log(), vec![(root(), 42u32)]);10301031			run_to_block(100);1032			assert_eq!(logger::log(), vec![(root(), 42u32)]);1033		});1034	}10351036	#[test]1037	fn reschedule_named_perodic_works() {1038		new_test_ext().execute_with(|| {1039			let call = Call::Logger(logger::Call::log(42, 1000));1040			assert!(!<Test as frame_system::Config>::BaseCallFilter::filter(&call));1041			assert_eq!(Scheduler::do_schedule_named(1042				1u32.encode(), DispatchTime::At(4), Some((3, 3)), 127, root(), call1043			).unwrap(), (4, 0));10441045			run_to_block(3);1046			assert!(logger::log().is_empty());10471048			assert_eq!(Scheduler::do_reschedule_named(1u32.encode(), DispatchTime::At(5)).unwrap(), (5, 0));1049			assert_eq!(Scheduler::do_reschedule_named(1u32.encode(), DispatchTime::At(6)).unwrap(), (6, 0));10501051			run_to_block(5);1052			assert!(logger::log().is_empty());10531054			run_to_block(6);1055			assert_eq!(logger::log(), vec![(root(), 42u32)]);10561057			assert_eq!(Scheduler::do_reschedule_named(1u32.encode(), DispatchTime::At(10)).unwrap(), (10, 0));10581059			run_to_block(9);1060			assert_eq!(logger::log(), vec![(root(), 42u32)]);10611062			run_to_block(10);1063			assert_eq!(logger::log(), vec![(root(), 42u32), (root(), 42u32)]);10641065			run_to_block(13);1066			assert_eq!(logger::log(), vec![(root(), 42u32), (root(), 42u32), (root(), 42u32)]);10671068			run_to_block(100);1069			assert_eq!(logger::log(), vec![(root(), 42u32), (root(), 42u32), (root(), 42u32)]);1070		});1071	}10721073	#[test]1074	fn cancel_named_scheduling_works_with_normal_cancel() {1075		new_test_ext().execute_with(|| {1076			// at #4.1077			Scheduler::do_schedule_named(1078				1u32.encode(), DispatchTime::At(4), None, 127, root(), Call::Logger(logger::Call::log(69, 1000))1079			).unwrap();1080			let i = Scheduler::do_schedule(1081				DispatchTime::At(4), None, 127, root(), Call::Logger(logger::Call::log(42, 1000))1082			).unwrap();1083			run_to_block(3);1084			assert!(logger::log().is_empty());1085			assert_ok!(Scheduler::do_cancel_named(None, 1u32.encode()));1086			assert_ok!(Scheduler::do_cancel(None, i));1087			run_to_block(100);1088			assert!(logger::log().is_empty());1089		});1090	}10911092	#[test]1093	fn cancel_named_periodic_scheduling_works() {1094		new_test_ext().execute_with(|| {1095			// at #4, every 3 blocks, 3 times.1096			Scheduler::do_schedule_named(1097				1u32.encode(),1098				DispatchTime::At(4),1099				Some((3, 3)),1100				127,1101				root(),1102				Call::Logger(logger::Call::log(42, 1000))1103			).unwrap();1104			// same id results in error.1105			assert!(Scheduler::do_schedule_named(1106				1u32.encode(),1107				DispatchTime::At(4),1108				None,1109				127,1110				root(),1111				Call::Logger(logger::Call::log(69, 1000))1112			).is_err());1113			// different id is ok.1114			Scheduler::do_schedule_named(1115				2u32.encode(), DispatchTime::At(8), None, 127, root(), Call::Logger(logger::Call::log(69, 1000))1116			).unwrap();1117			run_to_block(3);1118			assert!(logger::log().is_empty());1119			run_to_block(4);1120			assert_eq!(logger::log(), vec![(root(), 42u32)]);1121			run_to_block(6);1122			assert_ok!(Scheduler::do_cancel_named(None, 1u32.encode()));1123			run_to_block(100);1124			assert_eq!(logger::log(), vec![(root(), 42u32), (root(), 69u32)]);1125		});1126	}11271128	#[test]1129	fn scheduler_respects_weight_limits() {1130		new_test_ext().execute_with(|| {1131			assert_ok!(Scheduler::do_schedule(1132				DispatchTime::At(4),1133				None,1134				127,1135				root(),1136				Call::Logger(logger::Call::log(42, MaximumSchedulerWeight::get() / 2))1137			));1138			assert_ok!(Scheduler::do_schedule(1139				DispatchTime::At(4),1140				None,1141				127,1142				root(), Call::Logger(logger::Call::log(69, MaximumSchedulerWeight::get() / 2))1143			));1144			// 69 and 42 do not fit together1145			run_to_block(4);1146			assert_eq!(logger::log(), vec![(root(), 42u32)]);1147			run_to_block(5);1148			assert_eq!(logger::log(), vec![(root(), 42u32), (root(), 69u32)]);1149		});1150	}11511152	#[test]1153	fn scheduler_respects_hard_deadlines_more() {1154		new_test_ext().execute_with(|| {1155			assert_ok!(Scheduler::do_schedule(1156				DispatchTime::At(4),1157				None,1158				0,1159				root(),1160				Call::Logger(logger::Call::log(42, MaximumSchedulerWeight::get() / 2))1161			));1162			assert_ok!(Scheduler::do_schedule(1163				DispatchTime::At(4),1164				None,1165				0,1166				root(),1167				Call::Logger(logger::Call::log(69, MaximumSchedulerWeight::get() / 2))1168			));1169			// With base weights, 69 and 42 should not fit together, but do because of hard deadlines1170			run_to_block(4);1171			assert_eq!(logger::log(), vec![(root(), 42u32), (root(), 69u32)]);1172		});1173	}11741175	#[test]1176	fn scheduler_respects_priority_ordering() {1177		new_test_ext().execute_with(|| {1178			assert_ok!(Scheduler::do_schedule(1179				DispatchTime::At(4),1180				None,1181				1,1182				root(),1183				Call::Logger(logger::Call::log(42, MaximumSchedulerWeight::get() / 2))1184			));1185			assert_ok!(Scheduler::do_schedule(1186				DispatchTime::At(4),1187				None,1188				0,1189				root(),1190				Call::Logger(logger::Call::log(69, MaximumSchedulerWeight::get() / 2))1191			));1192			run_to_block(4);1193			assert_eq!(logger::log(), vec![(root(), 69u32), (root(), 42u32)]);1194		});1195	}11961197	#[test]1198	fn scheduler_respects_priority_ordering_with_soft_deadlines() {1199		new_test_ext().execute_with(|| {1200			assert_ok!(Scheduler::do_schedule(1201				DispatchTime::At(4),1202				None,1203				255,1204				root(), Call::Logger(logger::Call::log(42, MaximumSchedulerWeight::get() / 3))1205			));1206			assert_ok!(Scheduler::do_schedule(1207				DispatchTime::At(4),1208				None,1209				127,1210				root(), Call::Logger(logger::Call::log(69, MaximumSchedulerWeight::get() / 2))1211			));1212			assert_ok!(Scheduler::do_schedule(1213				DispatchTime::At(4),1214				None,1215				126,1216				root(), Call::Logger(logger::Call::log(2600, MaximumSchedulerWeight::get() / 2))1217			));12181219			// 2600 does not fit with 69 or 42, but has higher priority, so will go through1220			run_to_block(4);1221			assert_eq!(logger::log(), vec![(root(), 2600u32)]);1222			// 69 and 42 fit together1223			run_to_block(5);1224			assert_eq!(logger::log(), vec![(root(), 2600u32), (root(), 69u32), (root(), 42u32)]);1225		});1226	}12271228	#[test]1229	fn on_initialize_weight_is_correct() {1230		new_test_ext().execute_with(|| {1231			let base_weight: Weight = <Test as frame_system::Config>::DbWeight::get().reads_writes(1, 2);1232			let base_multiplier = 0;1233			let named_multiplier = <Test as frame_system::Config>::DbWeight::get().writes(1);1234			let periodic_multiplier = <Test as frame_system::Config>::DbWeight::get().reads_writes(1, 1);12351236			// Named1237			assert_ok!(1238				Scheduler::do_schedule_named(1239					1u32.encode(), DispatchTime::At(1), None, 255, root(),1240					Call::Logger(logger::Call::log(3, MaximumSchedulerWeight::get() / 3))1241				)1242			);1243			// Anon Periodic1244			assert_ok!(Scheduler::do_schedule(1245				DispatchTime::At(1),1246				Some((1000, 3)),1247				128,1248				root(),1249				Call::Logger(logger::Call::log(42, MaximumSchedulerWeight::get() / 3))1250			));1251			// Anon1252			assert_ok!(Scheduler::do_schedule(1253				DispatchTime::At(1),1254				None,1255				127,1256				root(),1257				Call::Logger(logger::Call::log(69, MaximumSchedulerWeight::get() / 2))1258			));1259			// Named Periodic1260			assert_ok!(Scheduler::do_schedule_named(1261				2u32.encode(), DispatchTime::At(1), Some((1000, 3)), 126, root(),1262				Call::Logger(logger::Call::log(2600, MaximumSchedulerWeight::get() / 2)))1263			);12641265			// Will include the named periodic only1266			let actual_weight = Scheduler::on_initialize(1);1267			let call_weight = MaximumSchedulerWeight::get() / 2;1268			assert_eq!(1269				actual_weight, call_weight + base_weight + base_multiplier + named_multiplier + periodic_multiplier1270			);1271			assert_eq!(logger::log(), vec![(root(), 2600u32)]);12721273			// Will include anon and anon periodic1274			let actual_weight = Scheduler::on_initialize(2);1275			let call_weight = MaximumSchedulerWeight::get() / 2 + MaximumSchedulerWeight::get() / 3;1276			assert_eq!(actual_weight, call_weight + base_weight + base_multiplier * 2 + periodic_multiplier);1277			assert_eq!(logger::log(), vec![(root(), 2600u32), (root(), 69u32), (root(), 42u32)]);12781279			// Will include named only1280			let actual_weight = Scheduler::on_initialize(3);1281			let call_weight = MaximumSchedulerWeight::get() / 3;1282			assert_eq!(actual_weight, call_weight + base_weight + base_multiplier + named_multiplier);1283			assert_eq!(logger::log(), vec![(root(), 2600u32), (root(), 69u32), (root(), 42u32), (root(), 3u32)]);12841285			// Will contain none1286			let actual_weight = Scheduler::on_initialize(4);1287			assert_eq!(actual_weight, 0);1288		});1289	}12901291	#[test]1292	fn root_calls_works() {1293		new_test_ext().execute_with(|| {1294			let call = Box::new(Call::Logger(logger::Call::log(69, 1000)));1295			let call2 = Box::new(Call::Logger(logger::Call::log(42, 1000)));1296			assert_ok!(Scheduler::schedule_named(Origin::root(), 1u32.encode(), 4, None, 127, call));1297			assert_ok!(Scheduler::schedule(Origin::root(), 4, None, 127, call2));1298			run_to_block(3);1299			// Scheduled calls are in the agenda.1300			assert_eq!(Agenda::<Test>::get(4).len(), 2);1301			assert!(logger::log().is_empty());1302			assert_ok!(Scheduler::cancel_named(Origin::root(), 1u32.encode()));1303			assert_ok!(Scheduler::cancel(Origin::root(), 4, 1));1304			// Scheduled calls are made NONE, so should not effect state1305			run_to_block(100);1306			assert!(logger::log().is_empty());1307		});1308	}13091310	#[test]1311	fn fails_to_schedule_task_in_the_past() {1312		new_test_ext().execute_with(|| {1313			run_to_block(3);13141315			let call = Box::new(Call::Logger(logger::Call::log(69, 1000)));1316			let call2 = Box::new(Call::Logger(logger::Call::log(42, 1000)));13171318			assert_err!(1319				Scheduler::schedule_named(Origin::root(), 1u32.encode(), 2, None, 127, call),1320				Error::<Test>::TargetBlockNumberInPast,1321			);13221323			assert_err!(1324				Scheduler::schedule(Origin::root(), 2, None, 127, call2.clone()),1325				Error::<Test>::TargetBlockNumberInPast,1326			);13271328			assert_err!(1329				Scheduler::schedule(Origin::root(), 3, None, 127, call2),1330				Error::<Test>::TargetBlockNumberInPast,1331			);1332		});1333	}13341335	#[test]1336	fn should_use_orign() {1337		new_test_ext().execute_with(|| {1338			let call = Box::new(Call::Logger(logger::Call::log(69, 1000)));1339			let call2 = Box::new(Call::Logger(logger::Call::log(42, 1000)));1340			assert_ok!(1341				Scheduler::schedule_named(system::RawOrigin::Signed(1).into(), 1u32.encode(), 4, None, 127, call)1342			);1343			assert_ok!(Scheduler::schedule(system::RawOrigin::Signed(1).into(), 4, None, 127, call2));1344			run_to_block(3);1345			// Scheduled calls are in the agenda.1346			assert_eq!(Agenda::<Test>::get(4).len(), 2);1347			assert!(logger::log().is_empty());1348			assert_ok!(Scheduler::cancel_named(system::RawOrigin::Signed(1).into(), 1u32.encode()));1349			assert_ok!(Scheduler::cancel(system::RawOrigin::Signed(1).into(), 4, 1));1350			// Scheduled calls are made NONE, so should not effect state1351			run_to_block(100);1352			assert!(logger::log().is_empty());1353		});1354	}13551356	#[test]1357	fn should_check_orign() {1358		new_test_ext().execute_with(|| {1359			let call = Box::new(Call::Logger(logger::Call::log(69, 1000)));1360			let call2 = Box::new(Call::Logger(logger::Call::log(42, 1000)));1361			assert_noop!(1362				Scheduler::schedule_named(system::RawOrigin::Signed(2).into(), 1u32.encode(), 4, None, 127, call),1363				BadOrigin1364			);1365			assert_noop!(Scheduler::schedule(system::RawOrigin::Signed(2).into(), 4, None, 127, call2), BadOrigin);1366		});1367	}13681369	#[test]1370	fn should_check_orign_for_cancel() {1371		new_test_ext().execute_with(|| {1372			let call = Box::new(Call::Logger(logger::Call::log_without_filter(69, 1000)));1373			let call2 = Box::new(Call::Logger(logger::Call::log_without_filter(42, 1000)));1374			assert_ok!(1375				Scheduler::schedule_named(system::RawOrigin::Signed(1).into(), 1u32.encode(), 4, None, 127, call)1376			);1377			assert_ok!(Scheduler::schedule(system::RawOrigin::Signed(1).into(), 4, None, 127, call2));1378			run_to_block(3);1379			// Scheduled calls are in the agenda.1380			assert_eq!(Agenda::<Test>::get(4).len(), 2);1381			assert!(logger::log().is_empty());1382			assert_noop!(Scheduler::cancel_named(system::RawOrigin::Signed(2).into(), 1u32.encode()), BadOrigin);1383			assert_noop!(Scheduler::cancel(system::RawOrigin::Signed(2).into(), 4, 1), BadOrigin);1384			assert_noop!(Scheduler::cancel_named(system::RawOrigin::Root.into(), 1u32.encode()), BadOrigin);1385			assert_noop!(Scheduler::cancel(system::RawOrigin::Root.into(), 4, 1), BadOrigin);1386			run_to_block(5);1387			assert_eq!(1388				logger::log(),1389				vec![(system::RawOrigin::Signed(1).into(), 69u32), (system::RawOrigin::Signed(1).into(), 42u32)]1390			);1391		});1392	}13931394	#[test]1395	fn migration_to_v2_works() {1396		new_test_ext().execute_with(|| {1397			for i in 0..3u64 {1398				let k = i.twox_64_concat();1399				let old = vec![1400					Some(ScheduledV1 {1401						maybe_id: None,1402						priority: i as u8 + 10,1403						call: Call::Logger(logger::Call::log(96, 100)),1404						maybe_periodic: None,1405					}),1406					None,1407					Some(ScheduledV1 {1408						maybe_id: Some(b"test".to_vec()),1409						priority: 123,1410						call: Call::Logger(logger::Call::log(69, 1000)),1411						maybe_periodic: Some((456u64, 10)),1412					}),1413				];1414				frame_support::migration::put_storage_value(1415					b"Scheduler",1416					b"Agenda",1417					&k,1418					old,1419				);1420			}14211422			assert_eq!(StorageVersion::get(), Releases::V1);14231424			assert!(Scheduler::migrate_v1_to_t2());14251426			assert_eq_uvec!(Agenda::<Test>::iter().collect::<Vec<_>>(), vec![1427				(1428					0,1429					vec![1430					Some(ScheduledV2 {1431						maybe_id: None,1432						priority: 10,1433						call: Call::Logger(logger::Call::log(96, 100)),1434						maybe_periodic: None,1435						origin: root(),1436						_phantom: PhantomData::<u64>::default(),1437					}),1438					None,1439					Some(ScheduledV2 {1440						maybe_id: Some(b"test".to_vec()),1441						priority: 123,1442						call: Call::Logger(logger::Call::log(69, 1000)),1443						maybe_periodic: Some((456u64, 10)),1444						origin: root(),1445						_phantom: PhantomData::<u64>::default(),1446					}),1447				]),1448				(1449					1,1450					vec![1451						Some(ScheduledV2 {1452							maybe_id: None,1453							priority: 11,1454							call: Call::Logger(logger::Call::log(96, 100)),1455							maybe_periodic: None,1456							origin: root(),1457							_phantom: PhantomData::<u64>::default(),1458						}),1459						None,1460						Some(ScheduledV2 {1461							maybe_id: Some(b"test".to_vec()),1462							priority: 123,1463							call: Call::Logger(logger::Call::log(69, 1000)),1464							maybe_periodic: Some((456u64, 10)),1465							origin: root(),1466							_phantom: PhantomData::<u64>::default(),1467						}),1468					]1469				),1470				(1471					2,1472					vec![1473						Some(ScheduledV2 {1474							maybe_id: None,1475							priority: 12,1476							call: Call::Logger(logger::Call::log(96, 100)),1477							maybe_periodic: None,1478							origin: root(),1479							_phantom: PhantomData::<u64>::default(),1480						}),1481						None,1482						Some(ScheduledV2 {1483							maybe_id: Some(b"test".to_vec()),1484							priority: 123,1485							call: Call::Logger(logger::Call::log(69, 1000)),1486							maybe_periodic: Some((456u64, 10)),1487							origin: root(),1488							_phantom: PhantomData::<u64>::default(),1489						}),1490					]1491				)1492			]);14931494			assert_eq!(StorageVersion::get(), Releases::V2);1495		});1496	}14971498	#[test]1499	fn test_migrate_origin() {1500		new_test_ext().execute_with(|| {1501			for i in 0..3u64 {1502				let k = i.twox_64_concat();1503				let old: Vec<Option<Scheduled<_, _, u32, u64>>> = vec![1504					Some(Scheduled {1505						maybe_id: None,1506						priority: i as u8 + 10,1507						call: Call::Logger(logger::Call::log(96, 100)),1508						origin: 3u32,1509						maybe_periodic: None,1510						_phantom: Default::default(),1511					}),1512					None,1513					Some(Scheduled {1514						maybe_id: Some(b"test".to_vec()),1515						priority: 123,1516						origin: 2u32,1517						call: Call::Logger(logger::Call::log(69, 1000)),1518						maybe_periodic: Some((456u64, 10)),1519						_phantom: Default::default(),1520					}),1521				];1522				frame_support::migration::put_storage_value(1523					b"Scheduler",1524					b"Agenda",1525					&k,1526					old,1527				);1528			}15291530			impl Into<OriginCaller> for u32 {1531				fn into(self) -> OriginCaller {1532					match self {1533						3u32 => system::RawOrigin::Root.into(),1534						2u32 => system::RawOrigin::None.into(),1535						_ => unreachable!("test make no use of it"),1536					}1537				}1538			}15391540			Scheduler::migrate_origin::<u32>();15411542			assert_eq_uvec!(Agenda::<Test>::iter().collect::<Vec<_>>(), vec![1543				(1544					0,1545					vec![1546					Some(ScheduledV2::<_, _, OriginCaller, u64> {1547						maybe_id: None,1548						priority: 10,1549						call: Call::Logger(logger::Call::log(96, 100)),1550						maybe_periodic: None,1551						origin: system::RawOrigin::Root.into(),1552						_phantom: PhantomData::<u64>::default(),1553					}),1554					None,1555					Some(ScheduledV2 {1556						maybe_id: Some(b"test".to_vec()),1557						priority: 123,1558						call: Call::Logger(logger::Call::log(69, 1000)),1559						maybe_periodic: Some((456u64, 10)),1560						origin: system::RawOrigin::None.into(),1561						_phantom: PhantomData::<u64>::default(),1562					}),1563				]),1564				(1565					1,1566					vec![1567						Some(ScheduledV2 {1568							maybe_id: None,1569							priority: 11,1570							call: Call::Logger(logger::Call::log(96, 100)),1571							maybe_periodic: None,1572							origin: system::RawOrigin::Root.into(),1573							_phantom: PhantomData::<u64>::default(),1574						}),1575						None,1576						Some(ScheduledV2 {1577							maybe_id: Some(b"test".to_vec()),1578							priority: 123,1579							call: Call::Logger(logger::Call::log(69, 1000)),1580							maybe_periodic: Some((456u64, 10)),1581							origin: system::RawOrigin::None.into(),1582							_phantom: PhantomData::<u64>::default(),1583						}),1584					]1585				),1586				(1587					2,1588					vec![1589						Some(ScheduledV2 {1590							maybe_id: None,1591							priority: 12,1592							call: Call::Logger(logger::Call::log(96, 100)),1593							maybe_periodic: None,1594							origin: system::RawOrigin::Root.into(),1595							_phantom: PhantomData::<u64>::default(),1596						}),1597						None,1598						Some(ScheduledV2 {1599							maybe_id: Some(b"test".to_vec()),1600							priority: 123,1601							call: Call::Logger(logger::Call::log(69, 1000)),1602							maybe_periodic: Some((456u64, 10)),1603							origin: system::RawOrigin::None.into(),1604							_phantom: PhantomData::<u64>::default(),1605						}),1606					]1607				)1608			]);1609		});1610	}1611}