git.delta.rocks / unique-network / refs/commits / 3fb62366cc89

difftreelog

source

pallets/scheduler/src/lib.rs50.9 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)]53#![allow(clippy::type_complexity, clippy::boxed_local, clippy::unused_unit)]5455mod benchmarking;56pub mod weights;5758use sp_std::{prelude::*, marker::PhantomData, borrow::Borrow};59use codec::{Encode, Decode, Codec};60use sp_runtime::{61	RuntimeDebug,62	traits::{Zero, One, BadOrigin, Saturating},63};64use frame_support::{65	decl_module, decl_storage, decl_event, decl_error, IterableStorageMap,66	dispatch::{Dispatchable, DispatchError, DispatchResult, Parameter},67	traits::{68		Get,69		schedule::{self, DispatchTime},70		OriginTrait, EnsureOrigin, IsType,71	},72	weights::{GetDispatchInfo, Weight},73};74use frame_system::{self as system, ensure_signed};75pub use weights::WeightInfo;76use up_sponsorship::SponsorshipHandler;7778/// Our pallet's configuration trait. All our types and constants go in here. If the79/// pallet is dependent on specific other pallets, then their configuration traits80/// should be added to our implied traits list.81///82/// `system::Config` should always be included in our implied traits.83/// //84pub trait Config: system::Config {85	/// The overarching event type.86	type Event: From<Event<Self>> + Into<<Self as system::Config>::Event>;8788	/// The aggregated origin which the dispatch will take.89	type Origin: OriginTrait<PalletsOrigin = Self::PalletsOrigin>90		+ From<Self::PalletsOrigin>91		+ IsType<<Self as system::Config>::Origin>;9293	/// The caller origin, overarching type of all pallets origins.94	type PalletsOrigin: From<system::RawOrigin<Self::AccountId>> + Codec + Clone + Eq;9596	/// The aggregated call type.97	type Call: Parameter98		+ Dispatchable<Origin = <Self as Config>::Origin>99		+ GetDispatchInfo100		+ From<system::Call<Self>>;101102	/// The maximum weight that may be scheduled per block for any dispatchables of less priority103	/// than `schedule::HARD_DEADLINE`.104	type MaximumWeight: Get<Weight>;105106	/// Required origin to schedule or cancel calls.107	type ScheduleOrigin: EnsureOrigin<<Self as system::Config>::Origin>;108109	/// The maximum number of scheduled calls in the queue for a single block.110	/// Not strictly enforced, but used for weight estimation.111	type MaxScheduledPerBlock: Get<u32>;112113	/// Sponsoring function114	type SponsorshipHandler: SponsorshipHandler<Self::AccountId, <Self as Config>::Call>;115116	/// Weight information for extrinsics in this pallet.117	type WeightInfo: WeightInfo;118}119120// pub type SelfWeightInfo<T> = <T as system::Config>::WeightInfo;121122/// Just a simple index for naming period tasks.123pub type PeriodicIndex = u32;124/// The location of a scheduled task that can be used to remove it.125pub type TaskAddress<BlockNumber> = (BlockNumber, u32);126127#[cfg_attr(any(feature = "std", test), derive(PartialEq, Eq))]128#[derive(Clone, RuntimeDebug, Encode, Decode)]129struct ScheduledV1<Call, BlockNumber> {130	maybe_id: Option<Vec<u8>>,131	priority: schedule::Priority,132	call: Call,133	maybe_periodic: Option<schedule::Period<BlockNumber>>,134}135136/// Information regarding an item to be executed in the future.137#[cfg_attr(any(feature = "std", test), derive(PartialEq, Eq))]138#[derive(Clone, RuntimeDebug, Encode, Decode)]139pub struct ScheduledV2<Call, BlockNumber, PalletsOrigin, AccountId> {140	/// The unique identity for this task, if there is one.141	maybe_id: Option<Vec<u8>>,142	/// This task's priority.143	priority: schedule::Priority,144	/// The call to be dispatched.145	call: Call,146	/// If the call is periodic, then this points to the information concerning that.147	maybe_periodic: Option<schedule::Period<BlockNumber>>,148	/// The origin to dispatch the call.149	origin: PalletsOrigin,150	_phantom: PhantomData<AccountId>,151}152153/// The current version of Scheduled struct.154pub type Scheduled<Call, BlockNumber, PalletsOrigin, AccountId> =155	ScheduledV2<Call, BlockNumber, PalletsOrigin, AccountId>;156157// A value placed in storage that represents the current version of the Scheduler storage.158// This value is used by the `on_runtime_upgrade` logic to determine whether we run159// storage migration logic.160#[derive(Encode, Decode, Clone, Copy, PartialEq, Eq, RuntimeDebug)]161enum Releases {162	V1,163	V2,164}165166impl Default for Releases {167	fn default() -> Self {168		Releases::V1169	}170}171172#[derive(Encode, Decode, Clone, PartialEq, Eq, RuntimeDebug)]173pub struct CallSpec {174	module: u32,175	method: u32,176}177178decl_storage! {179	trait Store for Module<T: Config> as Scheduler {180		/// Items to be executed, indexed by the block number that they should be executed on.181		pub Agenda: map hasher(twox_64_concat) T::BlockNumber182			=> Vec<Option<Scheduled<<T as Config>::Call, T::BlockNumber, T::PalletsOrigin, T::AccountId>>>;183184		pub SpecAgenda: map hasher(twox_64_concat) T::BlockNumber185			=> Vec<Option<CallSpec>>;186187		/// Lookup from identity to the block number and index of the task.188		Lookup: map hasher(twox_64_concat) Vec<u8> => Option<TaskAddress<T::BlockNumber>>;189190		/// Storage version of the pallet.191		///192		/// New networks start with last version.193		StorageVersion build(|_| Releases::V2): Releases;194	}195}196197decl_event!(198	pub enum Event<T> where <T as system::Config>::BlockNumber {199		/// Scheduled some task. \[when, index\]200		Scheduled(BlockNumber, u32),201		/// Canceled some task. \[when, index\]202		Canceled(BlockNumber, u32),203		/// Dispatched some task. \[task, id, result\]204		Dispatched(TaskAddress<BlockNumber>, Option<Vec<u8>>, DispatchResult),205	}206);207208decl_error! {209	pub enum Error for Module<T: Config> {210		/// Failed to schedule a call211		FailedToSchedule,212		/// Cannot find the scheduled call.213		NotFound,214		/// Given target block number is in the past.215		TargetBlockNumberInPast,216		/// Reschedule failed because it does not change scheduled time.217		RescheduleNoChange,218	}219}220221decl_module! {222	/// Scheduler module declaration.223	pub struct Module<T: Config> for enum Call224	where225		origin: <T as system::Config>::Origin226	{227		type Error = Error<T>;228		fn deposit_event() = default;229230231		/// Anonymously schedule a task.232		///233		/// # <weight>234		/// - S = Number of already scheduled calls235		/// - Base Weight: 22.29 + .126 * S µs236		/// - DB Weight:237		///     - Read: Agenda238		///     - Write: Agenda239		/// - Will use base weight of 25 which should be good for up to 30 scheduled calls240		/// # </weight>241		#[weight = <T as Config>::WeightInfo::schedule(T::MaxScheduledPerBlock::get())]242		fn schedule(origin,243			when: T::BlockNumber,244			maybe_periodic: Option<schedule::Period<T::BlockNumber>>,245			priority: schedule::Priority,246			call: Box<<T as Config>::Call>,247		)248		{249			let origin = <T as Config>::Origin::from(origin);250			Self::do_schedule(DispatchTime::At(when), maybe_periodic, priority, origin.caller().clone(), *call)?;251		}252253		/// Cancel an anonymously scheduled task.254		///255		/// # <weight>256		/// - S = Number of already scheduled calls257		/// - Base Weight: 22.15 + 2.869 * S µs258		/// - DB Weight:259		///     - Read: Agenda260		///     - Write: Agenda, Lookup261		/// - Will use base weight of 100 which should be good for up to 30 scheduled calls262		/// # </weight>263		#[weight = <T as Config>::WeightInfo::cancel(T::MaxScheduledPerBlock::get())]264		fn cancel(origin, when: T::BlockNumber, index: u32) {265			T::ScheduleOrigin::ensure_origin(origin.clone())?;266			let origin = <T as Config>::Origin::from(origin);267			Self::do_cancel(Some(origin.caller().clone()), (when, index))?;268		}269270		/// Schedule a named task.271		///272		/// # <weight>273		/// - S = Number of already scheduled calls274		/// - Base Weight: 29.6 + .159 * S µs275		/// - DB Weight:276		///     - Read: Agenda, Lookup277		///     - Write: Agenda, Lookup278		/// - Will use base weight of 35 which should be good for more than 30 scheduled calls279		/// # </weight>280		#[weight = <T as Config>::WeightInfo::schedule_named(T::MaxScheduledPerBlock::get())]281		fn schedule_named(origin,282			id: Vec<u8>,283			when: T::BlockNumber,284			maybe_periodic: Option<schedule::Period<T::BlockNumber>>,285			priority: schedule::Priority,286			call: Box<<T as Config>::Call>,287		) {288			T::ScheduleOrigin::ensure_origin(origin.clone())?;289			let origin = <T as Config>::Origin::from(origin);290			Self::do_schedule_named(291				id, DispatchTime::At(when), maybe_periodic, priority, origin.caller().clone(), *call292			)?;293		}294295		/// Cancel a named scheduled task.296		///297		/// # <weight>298		/// - S = Number of already scheduled calls299		/// - Base Weight: 24.91 + 2.907 * S µs300		/// - DB Weight:301		///     - Read: Agenda, Lookup302		///     - Write: Agenda, Lookup303		/// - Will use base weight of 100 which should be good for up to 30 scheduled calls304		/// # </weight>305		#[weight = <T as Config>::WeightInfo::cancel_named(T::MaxScheduledPerBlock::get())]306		fn cancel_named(origin, id: Vec<u8>) {307			T::ScheduleOrigin::ensure_origin(origin.clone())?;308			let origin = <T as Config>::Origin::from(origin);309			Self::do_cancel_named(Some(origin.caller().clone()), id)?;310		}311312		/// Anonymously schedule a task after a delay.313		///314		/// # <weight>315		/// Same as [`schedule`].316		/// # </weight>317		#[weight = <T as Config>::WeightInfo::schedule(T::MaxScheduledPerBlock::get())]318		fn schedule_after(origin,319			after: T::BlockNumber,320			maybe_periodic: Option<schedule::Period<T::BlockNumber>>,321			priority: schedule::Priority,322			call: Box<<T as Config>::Call>,323		) {324			T::ScheduleOrigin::ensure_origin(origin.clone())?;325			let origin = <T as Config>::Origin::from(origin);326			Self::do_schedule(327				DispatchTime::After(after), maybe_periodic, priority, origin.caller().clone(), *call328			)?;329		}330331		/// Schedule a named task after a delay.332		///333		/// # <weight>334		/// Same as [`schedule_named`].335		/// # </weight>336		#[weight = <T as Config>::WeightInfo::schedule_named(T::MaxScheduledPerBlock::get())]337		fn schedule_named_after(origin,338			id: Vec<u8>,339			after: T::BlockNumber,340			maybe_periodic: Option<schedule::Period<T::BlockNumber>>,341			priority: schedule::Priority,342			call: Box<<T as Config>::Call>,343		) {344			T::ScheduleOrigin::ensure_origin(origin.clone())?;345			let origin = <T as Config>::Origin::from(origin);346			Self::do_schedule_named(347				id, DispatchTime::After(after), maybe_periodic, priority, origin.caller().clone(), *call348			)?;349		}350351		/// Execute the scheduled calls352		///353		/// # <weight>354		/// - S = Number of already scheduled calls355		/// - N = Named scheduled calls356		/// - P = Periodic Calls357		/// - Base Weight: 9.243 + 23.45 * S µs358		/// - DB Weight:359		///     - Read: Agenda + Lookup * N + Agenda(Future) * P360		///     - Write: Agenda + Lookup * N  + Agenda(future) * P361		/// # </weight>362		fn on_initialize(now: T::BlockNumber) -> Weight {363			let limit = T::MaximumWeight::get();364			let mut queued = Agenda::<T>::take(now).into_iter()365				.enumerate()366				.filter_map(|(index, s)| s.map(|inner| (index as u32, inner)))367				.collect::<Vec<_>>();368			if queued.len() as u32 > T::MaxScheduledPerBlock::get() {369				log::warn!(370					target: "runtime::scheduler",371					"Warning: This block has more items queued in Scheduler than \372					expected from the runtime configuration. An update might be needed."373				);374			}375			queued.sort_by_key(|(_, s)| s.priority);376			let base_weight: Weight = T::DbWeight::get().reads_writes(1, 2); // Agenda + Agenda(next)377			let mut total_weight: Weight = 0;378			queued.into_iter()379				.enumerate()380				.scan(base_weight, |cumulative_weight, (order, (index, s))| {381					*cumulative_weight = cumulative_weight382						.saturating_add(s.call.get_dispatch_info().weight);383384					let origin = <<T as Config>::Origin as From<T::PalletsOrigin>>::from(385						s.origin.clone()386					).into();387388					if ensure_signed(origin).is_ok() {389						 // AccountData for inner call origin accountdata.390						*cumulative_weight = cumulative_weight391							.saturating_add(T::DbWeight::get().reads_writes(1, 1));392					}393394					if s.maybe_id.is_some() {395						// Remove/Modify Lookup396						*cumulative_weight = cumulative_weight.saturating_add(T::DbWeight::get().writes(1));397					}398					if s.maybe_periodic.is_some() {399						// Read/Write Agenda for future block400						*cumulative_weight = cumulative_weight.saturating_add(T::DbWeight::get().reads_writes(1, 1));401					}402403					Some((order, index, *cumulative_weight, s))404				})405				.filter_map(|(order, index, cumulative_weight, mut s)| {406					// 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					if s.priority <= schedule::HARD_DEADLINE || cumulative_weight <= limit || order == 0 {411412						let origin = <<T as Config>::Origin as From<T::PalletsOrigin>>::from(413							s.origin.clone()414						).into();415						let sender = ensure_signed(origin).unwrap_or_default();416						let who_will_pay = T::SponsorshipHandler::get_sponsor(&sender, &s.call).unwrap_or(sender);417						let sponsor = T::PalletsOrigin::from(system::RawOrigin::Signed(who_will_pay));418						let r = s.call.clone().dispatch(sponsor.into());419						let maybe_id = s.maybe_id.clone();420						if let Some((period, count)) = s.maybe_periodic {421							if count > 1 {422								s.maybe_periodic = Some((period, count - 1));423							} else {424								s.maybe_periodic = None;425							}426							let next = now + period;427							// If scheduled is named, place it's information in `Lookup`428							if let Some(ref id) = s.maybe_id {429								let next_index = Agenda::<T>::decode_len(now + period).unwrap_or(0);430								Lookup::<T>::insert(id, (next, next_index as u32));431							}432							Agenda::<T>::append(next, Some(s));433						} else if let Some(ref id) = s.maybe_id {434									  Lookup::<T>::remove(id);435								  }436						Self::deposit_event(RawEvent::Dispatched(437							(now, index),438							maybe_id,439							r.map(|_| ()).map_err(|e| e.error)440						));441						total_weight = cumulative_weight;442						None443					} else {444						Some(Some(s))445					}446				})447				.for_each(|unused| {448					let next = now + One::one();449					Agenda::<T>::append(next, unused);450				});451452			total_weight453		}454	}455}456457impl<T: Config> Module<T> {458	/// Migrate storage format from V1 to V2.459	/// Return true if migration is performed.460	pub fn migrate_v1_to_t2() -> bool {461		if StorageVersion::get() == Releases::V1 {462			StorageVersion::put(Releases::V2);463464			Agenda::<T>::translate::<465				Vec<Option<ScheduledV1<<T as Config>::Call, T::BlockNumber>>>,466				_,467			>(|_, agenda| {468				Some(469					agenda470						.into_iter()471						.map(|schedule| {472							schedule.map(|schedule| ScheduledV2 {473								maybe_id: schedule.maybe_id,474								priority: schedule.priority,475								call: schedule.call,476								maybe_periodic: schedule.maybe_periodic,477								origin: system::RawOrigin::Root.into(),478								_phantom: Default::default(),479							})480						})481						.collect::<Vec<_>>(),482				)483			});484485			true486		} else {487			false488		}489	}490491	/// Helper to migrate scheduler when the pallet origin type has changed.492	pub fn migrate_origin<OldOrigin: Into<T::PalletsOrigin> + codec::Decode>() {493		Agenda::<T>::translate::<494			Vec<Option<Scheduled<<T as Config>::Call, T::BlockNumber, OldOrigin, T::AccountId>>>,495			_,496		>(|_, agenda| {497			Some(498				agenda499					.into_iter()500					.map(|schedule| {501						schedule.map(|schedule| Scheduled {502							maybe_id: schedule.maybe_id,503							priority: schedule.priority,504							call: schedule.call,505							maybe_periodic: schedule.maybe_periodic,506							origin: schedule.origin.into(),507							_phantom: Default::default(),508						})509					})510					.collect::<Vec<_>>(),511			)512		});513	}514515	fn resolve_time(when: DispatchTime<T::BlockNumber>) -> Result<T::BlockNumber, DispatchError> {516		let now = frame_system::Pallet::<T>::block_number();517518		let when = match when {519			DispatchTime::At(x) => x,520			// The current block has already completed it's scheduled tasks, so521			// Schedule the task at lest one block after this current block.522			DispatchTime::After(x) => now.saturating_add(x).saturating_add(One::one()),523		};524525		if when <= now {526			return Err(Error::<T>::TargetBlockNumberInPast.into());527		}528529		Ok(when)530	}531532	fn do_schedule(533		when: DispatchTime<T::BlockNumber>,534		maybe_periodic: Option<schedule::Period<T::BlockNumber>>,535		priority: schedule::Priority,536		origin: T::PalletsOrigin,537		call: <T as Config>::Call,538	) -> Result<TaskAddress<T::BlockNumber>, DispatchError> {539		let when = Self::resolve_time(when)?;540541		// sanitize maybe_periodic542		let maybe_periodic = maybe_periodic543			.filter(|p| p.1 > 1 && !p.0.is_zero())544			// Remove one from the number of repetitions since we will schedule one now.545			.map(|(p, c)| (p, c - 1));546		let s = Some(Scheduled {547			maybe_id: None,548			priority,549			call,550			maybe_periodic,551			origin,552			_phantom: PhantomData::<T::AccountId>::default(),553		});554		Agenda::<T>::append(when, s);555		let index = Agenda::<T>::decode_len(when).unwrap_or(1) as u32 - 1;556		if index > T::MaxScheduledPerBlock::get() {557			log::warn!(558				target: "runtime::scheduler",559				"Warning: There are more items queued in the Scheduler than \560				expected from the runtime configuration. An update might be needed.",561			);562		}563		Self::deposit_event(RawEvent::Scheduled(when, index));564565		Ok((when, index))566	}567568	fn do_cancel(569		origin: Option<T::PalletsOrigin>,570		(when, index): TaskAddress<T::BlockNumber>,571	) -> Result<(), DispatchError> {572		let scheduled = Agenda::<T>::try_mutate(when, |agenda| {573			agenda.get_mut(index as usize).map_or(574				Ok(None),575				|s| -> Result<Option<Scheduled<_, _, _, _>>, DispatchError> {576					if let (Some(ref o), Some(ref s)) = (origin, s.borrow()) {577						if *o != s.origin {578							return Err(BadOrigin.into());579						}580					};581					Ok(s.take())582				},583			)584		})?;585		if let Some(s) = scheduled {586			if let Some(id) = s.maybe_id {587				Lookup::<T>::remove(id);588			}589			Self::deposit_event(RawEvent::Canceled(when, index));590			Ok(())591		} else {592			Err(Error::<T>::NotFound.into())593		}594	}595596	fn do_reschedule(597		(when, index): TaskAddress<T::BlockNumber>,598		new_time: DispatchTime<T::BlockNumber>,599	) -> Result<TaskAddress<T::BlockNumber>, DispatchError> {600		let new_time = Self::resolve_time(new_time)?;601602		if new_time == when {603			return Err(Error::<T>::RescheduleNoChange.into());604		}605606		Agenda::<T>::try_mutate(when, |agenda| -> DispatchResult {607			let task = agenda.get_mut(index as usize).ok_or(Error::<T>::NotFound)?;608			let task = task.take().ok_or(Error::<T>::NotFound)?;609			Agenda::<T>::append(new_time, Some(task));610			Ok(())611		})?;612613		let new_index = Agenda::<T>::decode_len(new_time).unwrap_or(1) as u32 - 1;614		Self::deposit_event(RawEvent::Canceled(when, index));615		Self::deposit_event(RawEvent::Scheduled(new_time, new_index));616617		Ok((new_time, new_index))618	}619620	fn do_schedule_named(621		id: Vec<u8>,622		when: DispatchTime<T::BlockNumber>,623		maybe_periodic: Option<schedule::Period<T::BlockNumber>>,624		priority: schedule::Priority,625		origin: T::PalletsOrigin,626		call: <T as Config>::Call,627	) -> Result<TaskAddress<T::BlockNumber>, DispatchError> {628		// ensure id it is unique629		if Lookup::<T>::contains_key(&id) {630			return Err(Error::<T>::FailedToSchedule.into());631		}632633		let when = Self::resolve_time(when)?;634635		// sanitize maybe_periodic636		let maybe_periodic = maybe_periodic637			.filter(|p| p.1 > 1 && !p.0.is_zero())638			// Remove one from the number of repetitions since we will schedule one now.639			.map(|(p, c)| (p, c - 1));640641		let s = Scheduled {642			maybe_id: Some(id.clone()),643			priority,644			call,645			maybe_periodic,646			origin,647			_phantom: Default::default(),648		};649		Agenda::<T>::append(when, Some(s));650		let index = Agenda::<T>::decode_len(when).unwrap_or(1) as u32 - 1;651		if index > T::MaxScheduledPerBlock::get() {652			log::warn!(653				target: "runtime::scheduler",654				"Warning: There are more items queued in the Scheduler than \655				expected from the runtime configuration. An update might be needed.",656			);657		}658		let address = (when, index);659		Lookup::<T>::insert(&id, &address);660		Self::deposit_event(RawEvent::Scheduled(when, index));661662		Ok(address)663	}664665	fn do_cancel_named(origin: Option<T::PalletsOrigin>, id: Vec<u8>) -> DispatchResult {666		Lookup::<T>::try_mutate_exists(id, |lookup| -> DispatchResult {667			if let Some((when, index)) = lookup.take() {668				let i = index as usize;669				Agenda::<T>::try_mutate(when, |agenda| -> DispatchResult {670					if let Some(s) = agenda.get_mut(i) {671						if let (Some(ref o), Some(ref s)) = (origin, s.borrow()) {672							if *o != s.origin {673								return Err(BadOrigin.into());674							}675						}676						*s = None;677					}678					Ok(())679				})?;680				Self::deposit_event(RawEvent::Canceled(when, index));681				Ok(())682			} else {683				Err(Error::<T>::NotFound.into())684			}685		})686	}687688	fn do_reschedule_named(689		id: Vec<u8>,690		new_time: DispatchTime<T::BlockNumber>,691	) -> Result<TaskAddress<T::BlockNumber>, DispatchError> {692		let new_time = Self::resolve_time(new_time)?;693694		Lookup::<T>::try_mutate_exists(695			id,696			|lookup| -> Result<TaskAddress<T::BlockNumber>, DispatchError> {697				let (when, index) = lookup.ok_or(Error::<T>::NotFound)?;698699				if new_time == when {700					return Err(Error::<T>::RescheduleNoChange.into());701				}702703				Agenda::<T>::try_mutate(when, |agenda| -> DispatchResult {704					let task = agenda.get_mut(index as usize).ok_or(Error::<T>::NotFound)?;705					let task = task.take().ok_or(Error::<T>::NotFound)?;706					Agenda::<T>::append(new_time, Some(task));707708					Ok(())709				})?;710711				let new_index = Agenda::<T>::decode_len(new_time).unwrap_or(1) as u32 - 1;712				Self::deposit_event(RawEvent::Canceled(when, index));713				Self::deposit_event(RawEvent::Scheduled(new_time, new_index));714715				*lookup = Some((new_time, new_index));716717				Ok((new_time, new_index))718			},719		)720	}721}722723impl<T: Config> schedule::Anon<T::BlockNumber, <T as Config>::Call, T::PalletsOrigin>724	for Module<T>725{726	type Address = TaskAddress<T::BlockNumber>;727728	fn schedule(729		when: DispatchTime<T::BlockNumber>,730		maybe_periodic: Option<schedule::Period<T::BlockNumber>>,731		priority: schedule::Priority,732		origin: T::PalletsOrigin,733		call: <T as Config>::Call,734	) -> Result<Self::Address, DispatchError> {735		Self::do_schedule(when, maybe_periodic, priority, origin, call)736	}737738	fn cancel((when, index): Self::Address) -> Result<(), ()> {739		Self::do_cancel(None, (when, index)).map_err(|_| ())740	}741742	fn reschedule(743		address: Self::Address,744		when: DispatchTime<T::BlockNumber>,745	) -> Result<Self::Address, DispatchError> {746		Self::do_reschedule(address, when)747	}748749	fn next_dispatch_time((when, index): Self::Address) -> Result<T::BlockNumber, ()> {750		Agenda::<T>::get(when)751			.get(index as usize)752			.ok_or(())753			.map(|_| when)754	}755}756757impl<T: Config> schedule::Named<T::BlockNumber, <T as Config>::Call, T::PalletsOrigin>758	for Module<T>759{760	type Address = TaskAddress<T::BlockNumber>;761762	fn schedule_named(763		id: Vec<u8>,764		when: DispatchTime<T::BlockNumber>,765		maybe_periodic: Option<schedule::Period<T::BlockNumber>>,766		priority: schedule::Priority,767		origin: T::PalletsOrigin,768		call: <T as Config>::Call,769	) -> Result<Self::Address, ()> {770		Self::do_schedule_named(id, when, maybe_periodic, priority, origin, call).map_err(|_| ())771	}772773	fn cancel_named(id: Vec<u8>) -> Result<(), ()> {774		Self::do_cancel_named(None, id).map_err(|_| ())775	}776777	fn reschedule_named(778		id: Vec<u8>,779		when: DispatchTime<T::BlockNumber>,780	) -> Result<Self::Address, DispatchError> {781		Self::do_reschedule_named(id, when)782	}783784	fn next_dispatch_time(id: Vec<u8>) -> Result<T::BlockNumber, ()> {785		Lookup::<T>::get(id)786			.and_then(|(when, index)| Agenda::<T>::get(when).get(index as usize).map(|_| when))787			.ok_or(())788	}789}790791#[cfg(test)]792#[allow(clippy::from_over_into)]793mod tests {794	use super::*;795796	use frame_support::{797		parameter_types, assert_ok, ord_parameter_types, assert_noop, assert_err, Hashable,798		traits::{OnInitialize, OnFinalize, Filter},799		weights::constants::RocksDbWeight,800	};801	use sp_core::H256;802	use sp_runtime::{803		Perbill,804		testing::Header,805		traits::{BlakeTwo256, IdentityLookup},806	};807	use frame_system::{EnsureOneOf, EnsureRoot, EnsureSignedBy};808	use substrate_test_utils::assert_eq_uvec;809	use crate as scheduler;810811	mod logger {812		use super::*;813		use std::cell::RefCell;814815		thread_local! {816			static LOG: RefCell<Vec<(OriginCaller, u32)>> = RefCell::new(Vec::new());817		}818		pub fn log() -> Vec<(OriginCaller, u32)> {819			LOG.with(|log| log.borrow().clone())820		}821		pub trait Config: system::Config {822			type Event: From<Event> + Into<<Self as system::Config>::Event>;823		}824		decl_event! {825			pub enum Event {826				Logged(u32, Weight),827			}828		}829		decl_module! {830			pub struct Module<T: Config> for enum Call831			where832				origin: <T as system::Config>::Origin,833				<T as system::Config>::Origin: OriginTrait<PalletsOrigin = OriginCaller>834			{835				fn deposit_event() = default;836837				#[weight = *weight]838				fn log(origin, i: u32, weight: Weight) {839					Self::deposit_event(Event::Logged(i, weight));840					LOG.with(|log| {841						log.borrow_mut().push((origin.caller().clone(), i));842					})843				}844845				#[weight = *weight]846				fn log_without_filter(origin, i: u32, weight: Weight) {847					Self::deposit_event(Event::Logged(i, weight));848					LOG.with(|log| {849						log.borrow_mut().push((origin.caller().clone(), i));850					})851				}852			}853		}854	}855856	type UncheckedExtrinsic = frame_system::mocking::MockUncheckedExtrinsic<Test>;857	type Block = frame_system::mocking::MockBlock<Test>;858859	frame_support::construct_runtime!(860		pub enum Test where861			Block = Block,862			NodeBlock = Block,863			UncheckedExtrinsic = UncheckedExtrinsic,864		{865			System: frame_system::{Pallet, Call, Config, Storage, Event<T>},866			Logger: logger::{Pallet, Call, Event},867			Scheduler: scheduler::{Pallet, Call, Storage, Event<T>},868		}869	);870871	// Scheduler must dispatch with root and no filter, this tests base filter is indeed not used.872	pub struct BaseFilter;873	impl Filter<Call> for BaseFilter {874		fn filter(call: &Call) -> bool {875			!matches!(call, Call::Logger(logger::Call::log(_, _)))876		}877	}878879	parameter_types! {880		pub const BlockHashCount: u64 = 250;881		pub BlockWeights: frame_system::limits::BlockWeights =882			frame_system::limits::BlockWeights::simple_max(2_000_000_000_000);883	}884	impl system::Config for Test {885		type BaseCallFilter = BaseFilter;886		type BlockWeights = ();887		type BlockLength = ();888		type DbWeight = RocksDbWeight;889		type Origin = Origin;890		type Call = Call;891		type Index = u64;892		type BlockNumber = u64;893		type Hash = H256;894		type Hashing = BlakeTwo256;895		type AccountId = u64;896		type Lookup = IdentityLookup<Self::AccountId>;897		type Header = Header;898		type Event = Event;899		type BlockHashCount = BlockHashCount;900		type Version = ();901		type PalletInfo = PalletInfo;902		type AccountData = ();903		type OnNewAccount = ();904		type OnKilledAccount = ();905		type SystemWeightInfo = ();906		type SS58Prefix = ();907		type OnSetCode = ();908	}909	impl logger::Config for Test {910		type Event = Event;911	}912	parameter_types! {913		pub MaximumSchedulerWeight: Weight = Perbill::from_percent(80) * BlockWeights::get().max_block;914		pub const MaxScheduledPerBlock: u32 = 10;915	}916	ord_parameter_types! {917		pub const One: u64 = 1;918	}919920	impl Config for Test {921		type Event = Event;922		type Origin = Origin;923		type PalletsOrigin = OriginCaller;924		type Call = Call;925		type MaximumWeight = MaximumSchedulerWeight;926		type ScheduleOrigin = EnsureOneOf<u64, EnsureRoot<u64>, EnsureSignedBy<One, u64>>;927		type MaxScheduledPerBlock = MaxScheduledPerBlock;928		type WeightInfo = ();929		type SponsorshipHandler = ();930	}931932	pub fn new_test_ext() -> sp_io::TestExternalities {933		let t = system::GenesisConfig::default()934			.build_storage::<Test>()935			.unwrap();936		t.into()937	}938939	fn run_to_block(n: u64) {940		while System::block_number() < n {941			Scheduler::on_finalize(System::block_number());942			System::set_block_number(System::block_number() + 1);943			Scheduler::on_initialize(System::block_number());944		}945	}946947	fn root() -> OriginCaller {948		system::RawOrigin::Root.into()949	}950951	#[test]952	fn basic_scheduling_works() {953		new_test_ext().execute_with(|| {954			let call = Call::Logger(logger::Call::log(42, 1000));955			assert!(!<Test as frame_system::Config>::BaseCallFilter::filter(956				&call957			));958			assert_ok!(Scheduler::do_schedule(959				DispatchTime::At(4),960				None,961				127,962				root(),963				call964			));965			run_to_block(3);966			assert!(logger::log().is_empty());967			run_to_block(4);968			assert_eq!(logger::log(), vec![(root(), 42u32)]);969			run_to_block(100);970			assert_eq!(logger::log(), vec![(root(), 42u32)]);971		});972	}973974	#[test]975	fn schedule_after_works() {976		new_test_ext().execute_with(|| {977			run_to_block(2);978			let call = Call::Logger(logger::Call::log(42, 1000));979			assert!(!<Test as frame_system::Config>::BaseCallFilter::filter(980				&call981			));982			// This will schedule the call 3 blocks after the next block... so block 3 + 3 = 6983			assert_ok!(Scheduler::do_schedule(984				DispatchTime::After(3),985				None,986				127,987				root(),988				call989			));990			run_to_block(5);991			assert!(logger::log().is_empty());992			run_to_block(6);993			assert_eq!(logger::log(), vec![(root(), 42u32)]);994			run_to_block(100);995			assert_eq!(logger::log(), vec![(root(), 42u32)]);996		});997	}998999	#[test]1000	fn schedule_after_zero_works() {1001		new_test_ext().execute_with(|| {1002			run_to_block(2);1003			let call = Call::Logger(logger::Call::log(42, 1000));1004			assert!(!<Test as frame_system::Config>::BaseCallFilter::filter(1005				&call1006			));1007			assert_ok!(Scheduler::do_schedule(1008				DispatchTime::After(0),1009				None,1010				127,1011				root(),1012				call1013			));1014			// Will trigger on the next block.1015			run_to_block(3);1016			assert_eq!(logger::log(), vec![(root(), 42u32)]);1017			run_to_block(100);1018			assert_eq!(logger::log(), vec![(root(), 42u32)]);1019		});1020	}10211022	#[test]1023	fn periodic_scheduling_works() {1024		new_test_ext().execute_with(|| {1025			// at #4, every 3 blocks, 3 times.1026			assert_ok!(Scheduler::do_schedule(1027				DispatchTime::At(4),1028				Some((3, 3)),1029				127,1030				root(),1031				Call::Logger(logger::Call::log(42, 1000))1032			));1033			run_to_block(3);1034			assert!(logger::log().is_empty());1035			run_to_block(4);1036			assert_eq!(logger::log(), vec![(root(), 42u32)]);1037			run_to_block(6);1038			assert_eq!(logger::log(), vec![(root(), 42u32)]);1039			run_to_block(7);1040			assert_eq!(logger::log(), vec![(root(), 42u32), (root(), 42u32)]);1041			run_to_block(9);1042			assert_eq!(logger::log(), vec![(root(), 42u32), (root(), 42u32)]);1043			run_to_block(10);1044			assert_eq!(1045				logger::log(),1046				vec![(root(), 42u32), (root(), 42u32), (root(), 42u32)]1047			);1048			run_to_block(100);1049			assert_eq!(1050				logger::log(),1051				vec![(root(), 42u32), (root(), 42u32), (root(), 42u32)]1052			);1053		});1054	}10551056	#[test]1057	fn reschedule_works() {1058		new_test_ext().execute_with(|| {1059			let call = Call::Logger(logger::Call::log(42, 1000));1060			assert!(!<Test as frame_system::Config>::BaseCallFilter::filter(1061				&call1062			));1063			assert_eq!(1064				Scheduler::do_schedule(DispatchTime::At(4), None, 127, root(), call).unwrap(),1065				(4, 0)1066			);10671068			run_to_block(3);1069			assert!(logger::log().is_empty());10701071			assert_eq!(1072				Scheduler::do_reschedule((4, 0), DispatchTime::At(6)).unwrap(),1073				(6, 0)1074			);10751076			assert_noop!(1077				Scheduler::do_reschedule((6, 0), DispatchTime::At(6)),1078				Error::<Test>::RescheduleNoChange1079			);10801081			run_to_block(4);1082			assert!(logger::log().is_empty());10831084			run_to_block(6);1085			assert_eq!(logger::log(), vec![(root(), 42u32)]);10861087			run_to_block(100);1088			assert_eq!(logger::log(), vec![(root(), 42u32)]);1089		});1090	}10911092	#[test]1093	fn reschedule_named_works() {1094		new_test_ext().execute_with(|| {1095			let call = Call::Logger(logger::Call::log(42, 1000));1096			assert!(!<Test as frame_system::Config>::BaseCallFilter::filter(1097				&call1098			));1099			assert_eq!(1100				Scheduler::do_schedule_named(1101					1u32.encode(),1102					DispatchTime::At(4),1103					None,1104					127,1105					root(),1106					call1107				)1108				.unwrap(),1109				(4, 0)1110			);11111112			run_to_block(3);1113			assert!(logger::log().is_empty());11141115			assert_eq!(1116				Scheduler::do_reschedule_named(1u32.encode(), DispatchTime::At(6)).unwrap(),1117				(6, 0)1118			);11191120			assert_noop!(1121				Scheduler::do_reschedule_named(1u32.encode(), DispatchTime::At(6)),1122				Error::<Test>::RescheduleNoChange1123			);11241125			run_to_block(4);1126			assert!(logger::log().is_empty());11271128			run_to_block(6);1129			assert_eq!(logger::log(), vec![(root(), 42u32)]);11301131			run_to_block(100);1132			assert_eq!(logger::log(), vec![(root(), 42u32)]);1133		});1134	}11351136	#[test]1137	fn reschedule_named_perodic_works() {1138		new_test_ext().execute_with(|| {1139			let call = Call::Logger(logger::Call::log(42, 1000));1140			assert!(!<Test as frame_system::Config>::BaseCallFilter::filter(1141				&call1142			));1143			assert_eq!(1144				Scheduler::do_schedule_named(1145					1u32.encode(),1146					DispatchTime::At(4),1147					Some((3, 3)),1148					127,1149					root(),1150					call1151				)1152				.unwrap(),1153				(4, 0)1154			);11551156			run_to_block(3);1157			assert!(logger::log().is_empty());11581159			assert_eq!(1160				Scheduler::do_reschedule_named(1u32.encode(), DispatchTime::At(5)).unwrap(),1161				(5, 0)1162			);1163			assert_eq!(1164				Scheduler::do_reschedule_named(1u32.encode(), DispatchTime::At(6)).unwrap(),1165				(6, 0)1166			);11671168			run_to_block(5);1169			assert!(logger::log().is_empty());11701171			run_to_block(6);1172			assert_eq!(logger::log(), vec![(root(), 42u32)]);11731174			assert_eq!(1175				Scheduler::do_reschedule_named(1u32.encode(), DispatchTime::At(10)).unwrap(),1176				(10, 0)1177			);11781179			run_to_block(9);1180			assert_eq!(logger::log(), vec![(root(), 42u32)]);11811182			run_to_block(10);1183			assert_eq!(logger::log(), vec![(root(), 42u32), (root(), 42u32)]);11841185			run_to_block(13);1186			assert_eq!(1187				logger::log(),1188				vec![(root(), 42u32), (root(), 42u32), (root(), 42u32)]1189			);11901191			run_to_block(100);1192			assert_eq!(1193				logger::log(),1194				vec![(root(), 42u32), (root(), 42u32), (root(), 42u32)]1195			);1196		});1197	}11981199	#[test]1200	fn cancel_named_scheduling_works_with_normal_cancel() {1201		new_test_ext().execute_with(|| {1202			// at #4.1203			Scheduler::do_schedule_named(1204				1u32.encode(),1205				DispatchTime::At(4),1206				None,1207				127,1208				root(),1209				Call::Logger(logger::Call::log(69, 1000)),1210			)1211			.unwrap();1212			let i = Scheduler::do_schedule(1213				DispatchTime::At(4),1214				None,1215				127,1216				root(),1217				Call::Logger(logger::Call::log(42, 1000)),1218			)1219			.unwrap();1220			run_to_block(3);1221			assert!(logger::log().is_empty());1222			assert_ok!(Scheduler::do_cancel_named(None, 1u32.encode()));1223			assert_ok!(Scheduler::do_cancel(None, i));1224			run_to_block(100);1225			assert!(logger::log().is_empty());1226		});1227	}12281229	#[test]1230	fn cancel_named_periodic_scheduling_works() {1231		new_test_ext().execute_with(|| {1232			// at #4, every 3 blocks, 3 times.1233			Scheduler::do_schedule_named(1234				1u32.encode(),1235				DispatchTime::At(4),1236				Some((3, 3)),1237				127,1238				root(),1239				Call::Logger(logger::Call::log(42, 1000)),1240			)1241			.unwrap();1242			// same id results in error.1243			assert!(Scheduler::do_schedule_named(1244				1u32.encode(),1245				DispatchTime::At(4),1246				None,1247				127,1248				root(),1249				Call::Logger(logger::Call::log(69, 1000))1250			)1251			.is_err());1252			// different id is ok.1253			Scheduler::do_schedule_named(1254				2u32.encode(),1255				DispatchTime::At(8),1256				None,1257				127,1258				root(),1259				Call::Logger(logger::Call::log(69, 1000)),1260			)1261			.unwrap();1262			run_to_block(3);1263			assert!(logger::log().is_empty());1264			run_to_block(4);1265			assert_eq!(logger::log(), vec![(root(), 42u32)]);1266			run_to_block(6);1267			assert_ok!(Scheduler::do_cancel_named(None, 1u32.encode()));1268			run_to_block(100);1269			assert_eq!(logger::log(), vec![(root(), 42u32), (root(), 69u32)]);1270		});1271	}12721273	#[test]1274	fn scheduler_respects_weight_limits() {1275		new_test_ext().execute_with(|| {1276			assert_ok!(Scheduler::do_schedule(1277				DispatchTime::At(4),1278				None,1279				127,1280				root(),1281				Call::Logger(logger::Call::log(42, MaximumSchedulerWeight::get() / 2))1282			));1283			assert_ok!(Scheduler::do_schedule(1284				DispatchTime::At(4),1285				None,1286				127,1287				root(),1288				Call::Logger(logger::Call::log(69, MaximumSchedulerWeight::get() / 2))1289			));1290			// 69 and 42 do not fit together1291			run_to_block(4);1292			assert_eq!(logger::log(), vec![(root(), 42u32)]);1293			run_to_block(5);1294			assert_eq!(logger::log(), vec![(root(), 42u32), (root(), 69u32)]);1295		});1296	}12971298	#[test]1299	fn scheduler_respects_hard_deadlines_more() {1300		new_test_ext().execute_with(|| {1301			assert_ok!(Scheduler::do_schedule(1302				DispatchTime::At(4),1303				None,1304				0,1305				root(),1306				Call::Logger(logger::Call::log(42, MaximumSchedulerWeight::get() / 2))1307			));1308			assert_ok!(Scheduler::do_schedule(1309				DispatchTime::At(4),1310				None,1311				0,1312				root(),1313				Call::Logger(logger::Call::log(69, MaximumSchedulerWeight::get() / 2))1314			));1315			// With base weights, 69 and 42 should not fit together, but do because of hard deadlines1316			run_to_block(4);1317			assert_eq!(logger::log(), vec![(root(), 42u32), (root(), 69u32)]);1318		});1319	}13201321	#[test]1322	fn scheduler_respects_priority_ordering() {1323		new_test_ext().execute_with(|| {1324			assert_ok!(Scheduler::do_schedule(1325				DispatchTime::At(4),1326				None,1327				1,1328				root(),1329				Call::Logger(logger::Call::log(42, MaximumSchedulerWeight::get() / 2))1330			));1331			assert_ok!(Scheduler::do_schedule(1332				DispatchTime::At(4),1333				None,1334				0,1335				root(),1336				Call::Logger(logger::Call::log(69, MaximumSchedulerWeight::get() / 2))1337			));1338			run_to_block(4);1339			assert_eq!(logger::log(), vec![(root(), 69u32), (root(), 42u32)]);1340		});1341	}13421343	#[test]1344	fn scheduler_respects_priority_ordering_with_soft_deadlines() {1345		new_test_ext().execute_with(|| {1346			assert_ok!(Scheduler::do_schedule(1347				DispatchTime::At(4),1348				None,1349				255,1350				root(),1351				Call::Logger(logger::Call::log(42, MaximumSchedulerWeight::get() / 3))1352			));1353			assert_ok!(Scheduler::do_schedule(1354				DispatchTime::At(4),1355				None,1356				127,1357				root(),1358				Call::Logger(logger::Call::log(69, MaximumSchedulerWeight::get() / 2))1359			));1360			assert_ok!(Scheduler::do_schedule(1361				DispatchTime::At(4),1362				None,1363				126,1364				root(),1365				Call::Logger(logger::Call::log(2600, MaximumSchedulerWeight::get() / 2))1366			));13671368			// 2600 does not fit with 69 or 42, but has higher priority, so will go through1369			run_to_block(4);1370			assert_eq!(logger::log(), vec![(root(), 2600u32)]);1371			// 69 and 42 fit together1372			run_to_block(5);1373			assert_eq!(1374				logger::log(),1375				vec![(root(), 2600u32), (root(), 69u32), (root(), 42u32)]1376			);1377		});1378	}13791380	#[test]1381	fn on_initialize_weight_is_correct() {1382		new_test_ext().execute_with(|| {1383			let base_weight: Weight =1384				<Test as frame_system::Config>::DbWeight::get().reads_writes(1, 2);1385			let base_multiplier = 0;1386			let named_multiplier = <Test as frame_system::Config>::DbWeight::get().writes(1);1387			let periodic_multiplier =1388				<Test as frame_system::Config>::DbWeight::get().reads_writes(1, 1);13891390			// Named1391			assert_ok!(Scheduler::do_schedule_named(1392				1u32.encode(),1393				DispatchTime::At(1),1394				None,1395				255,1396				root(),1397				Call::Logger(logger::Call::log(3, MaximumSchedulerWeight::get() / 3))1398			));1399			// Anon Periodic1400			assert_ok!(Scheduler::do_schedule(1401				DispatchTime::At(1),1402				Some((1000, 3)),1403				128,1404				root(),1405				Call::Logger(logger::Call::log(42, MaximumSchedulerWeight::get() / 3))1406			));1407			// Anon1408			assert_ok!(Scheduler::do_schedule(1409				DispatchTime::At(1),1410				None,1411				127,1412				root(),1413				Call::Logger(logger::Call::log(69, MaximumSchedulerWeight::get() / 2))1414			));1415			// Named Periodic1416			assert_ok!(Scheduler::do_schedule_named(1417				2u32.encode(),1418				DispatchTime::At(1),1419				Some((1000, 3)),1420				126,1421				root(),1422				Call::Logger(logger::Call::log(2600, MaximumSchedulerWeight::get() / 2))1423			));14241425			// Will include the named periodic only1426			let actual_weight = Scheduler::on_initialize(1);1427			let call_weight = MaximumSchedulerWeight::get() / 2;1428			assert_eq!(1429				actual_weight,1430				call_weight1431					+ base_weight + base_multiplier1432					+ named_multiplier + periodic_multiplier1433			);1434			assert_eq!(logger::log(), vec![(root(), 2600u32)]);14351436			// Will include anon and anon periodic1437			let actual_weight = Scheduler::on_initialize(2);1438			let call_weight = MaximumSchedulerWeight::get() / 2 + MaximumSchedulerWeight::get() / 3;1439			assert_eq!(1440				actual_weight,1441				call_weight + base_weight + base_multiplier * 2 + periodic_multiplier1442			);1443			assert_eq!(1444				logger::log(),1445				vec![(root(), 2600u32), (root(), 69u32), (root(), 42u32)]1446			);14471448			// Will include named only1449			let actual_weight = Scheduler::on_initialize(3);1450			let call_weight = MaximumSchedulerWeight::get() / 3;1451			assert_eq!(1452				actual_weight,1453				call_weight + base_weight + base_multiplier + named_multiplier1454			);1455			assert_eq!(1456				logger::log(),1457				vec![1458					(root(), 2600u32),1459					(root(), 69u32),1460					(root(), 42u32),1461					(root(), 3u32)1462				]1463			);14641465			// Will contain none1466			let actual_weight = Scheduler::on_initialize(4);1467			assert_eq!(actual_weight, 0);1468		});1469	}14701471	#[test]1472	fn root_calls_works() {1473		new_test_ext().execute_with(|| {1474			let call = Box::new(Call::Logger(logger::Call::log(69, 1000)));1475			let call2 = Box::new(Call::Logger(logger::Call::log(42, 1000)));1476			assert_ok!(Scheduler::schedule_named(1477				Origin::root(),1478				1u32.encode(),1479				4,1480				None,1481				127,1482				call1483			));1484			assert_ok!(Scheduler::schedule(Origin::root(), 4, None, 127, call2));1485			run_to_block(3);1486			// Scheduled calls are in the agenda.1487			assert_eq!(Agenda::<Test>::get(4).len(), 2);1488			assert!(logger::log().is_empty());1489			assert_ok!(Scheduler::cancel_named(Origin::root(), 1u32.encode()));1490			assert_ok!(Scheduler::cancel(Origin::root(), 4, 1));1491			// Scheduled calls are made NONE, so should not effect state1492			run_to_block(100);1493			assert!(logger::log().is_empty());1494		});1495	}14961497	#[test]1498	fn fails_to_schedule_task_in_the_past() {1499		new_test_ext().execute_with(|| {1500			run_to_block(3);15011502			let call = Box::new(Call::Logger(logger::Call::log(69, 1000)));1503			let call2 = Box::new(Call::Logger(logger::Call::log(42, 1000)));15041505			assert_err!(1506				Scheduler::schedule_named(Origin::root(), 1u32.encode(), 2, None, 127, call),1507				Error::<Test>::TargetBlockNumberInPast,1508			);15091510			assert_err!(1511				Scheduler::schedule(Origin::root(), 2, None, 127, call2.clone()),1512				Error::<Test>::TargetBlockNumberInPast,1513			);15141515			assert_err!(1516				Scheduler::schedule(Origin::root(), 3, None, 127, call2),1517				Error::<Test>::TargetBlockNumberInPast,1518			);1519		});1520	}15211522	#[test]1523	fn should_use_orign() {1524		new_test_ext().execute_with(|| {1525			let call = Box::new(Call::Logger(logger::Call::log(69, 1000)));1526			let call2 = Box::new(Call::Logger(logger::Call::log(42, 1000)));1527			assert_ok!(Scheduler::schedule_named(1528				system::RawOrigin::Signed(1).into(),1529				1u32.encode(),1530				4,1531				None,1532				127,1533				call1534			));1535			assert_ok!(Scheduler::schedule(1536				system::RawOrigin::Signed(1).into(),1537				4,1538				None,1539				127,1540				call21541			));1542			run_to_block(3);1543			// Scheduled calls are in the agenda.1544			assert_eq!(Agenda::<Test>::get(4).len(), 2);1545			assert!(logger::log().is_empty());1546			assert_ok!(Scheduler::cancel_named(1547				system::RawOrigin::Signed(1).into(),1548				1u32.encode()1549			));1550			assert_ok!(Scheduler::cancel(system::RawOrigin::Signed(1).into(), 4, 1));1551			// Scheduled calls are made NONE, so should not effect state1552			run_to_block(100);1553			assert!(logger::log().is_empty());1554		});1555	}15561557	#[test]1558	fn should_check_orign() {1559		new_test_ext().execute_with(|| {1560			let call = Box::new(Call::Logger(logger::Call::log(69, 1000)));1561			let call2 = Box::new(Call::Logger(logger::Call::log(42, 1000)));1562			assert_noop!(1563				Scheduler::schedule_named(1564					system::RawOrigin::Signed(2).into(),1565					1u32.encode(),1566					4,1567					None,1568					127,1569					call1570				),1571				BadOrigin1572			);1573			assert_noop!(1574				Scheduler::schedule(system::RawOrigin::Signed(2).into(), 4, None, 127, call2),1575				BadOrigin1576			);1577		});1578	}15791580	#[test]1581	fn should_check_orign_for_cancel() {1582		new_test_ext().execute_with(|| {1583			let call = Box::new(Call::Logger(logger::Call::log_without_filter(69, 1000)));1584			let call2 = Box::new(Call::Logger(logger::Call::log_without_filter(42, 1000)));1585			assert_ok!(Scheduler::schedule_named(1586				system::RawOrigin::Signed(1).into(),1587				1u32.encode(),1588				4,1589				None,1590				127,1591				call1592			));1593			assert_ok!(Scheduler::schedule(1594				system::RawOrigin::Signed(1).into(),1595				4,1596				None,1597				127,1598				call21599			));1600			run_to_block(3);1601			// Scheduled calls are in the agenda.1602			assert_eq!(Agenda::<Test>::get(4).len(), 2);1603			assert!(logger::log().is_empty());1604			assert_noop!(1605				Scheduler::cancel_named(system::RawOrigin::Signed(2).into(), 1u32.encode()),1606				BadOrigin1607			);1608			assert_noop!(1609				Scheduler::cancel(system::RawOrigin::Signed(2).into(), 4, 1),1610				BadOrigin1611			);1612			assert_noop!(1613				Scheduler::cancel_named(system::RawOrigin::Root.into(), 1u32.encode()),1614				BadOrigin1615			);1616			assert_noop!(1617				Scheduler::cancel(system::RawOrigin::Root.into(), 4, 1),1618				BadOrigin1619			);1620			run_to_block(5);1621			assert_eq!(1622				logger::log(),1623				vec![1624					(system::RawOrigin::Signed(1).into(), 69u32),1625					(system::RawOrigin::Signed(1).into(), 42u32)1626				]1627			);1628		});1629	}16301631	#[test]1632	fn migration_to_v2_works() {1633		new_test_ext().execute_with(|| {1634			for i in 0..3u64 {1635				let k = i.twox_64_concat();1636				let old = vec![1637					Some(ScheduledV1 {1638						maybe_id: None,1639						priority: i as u8 + 10,1640						call: Call::Logger(logger::Call::log(96, 100)),1641						maybe_periodic: None,1642					}),1643					None,1644					Some(ScheduledV1 {1645						maybe_id: Some(b"test".to_vec()),1646						priority: 123,1647						call: Call::Logger(logger::Call::log(69, 1000)),1648						maybe_periodic: Some((456u64, 10)),1649					}),1650				];1651				frame_support::migration::put_storage_value(b"Scheduler", b"Agenda", &k, old);1652			}16531654			assert_eq!(StorageVersion::get(), Releases::V1);16551656			assert!(Scheduler::migrate_v1_to_t2());16571658			assert_eq_uvec!(1659				Agenda::<Test>::iter().collect::<Vec<_>>(),1660				vec![1661					(1662						0,1663						vec![1664							Some(ScheduledV2 {1665								maybe_id: None,1666								priority: 10,1667								call: Call::Logger(logger::Call::log(96, 100)),1668								maybe_periodic: None,1669								origin: root(),1670								_phantom: PhantomData::<u64>::default(),1671							}),1672							None,1673							Some(ScheduledV2 {1674								maybe_id: Some(b"test".to_vec()),1675								priority: 123,1676								call: Call::Logger(logger::Call::log(69, 1000)),1677								maybe_periodic: Some((456u64, 10)),1678								origin: root(),1679								_phantom: PhantomData::<u64>::default(),1680							}),1681						]1682					),1683					(1684						1,1685						vec![1686							Some(ScheduledV2 {1687								maybe_id: None,1688								priority: 11,1689								call: Call::Logger(logger::Call::log(96, 100)),1690								maybe_periodic: None,1691								origin: root(),1692								_phantom: PhantomData::<u64>::default(),1693							}),1694							None,1695							Some(ScheduledV2 {1696								maybe_id: Some(b"test".to_vec()),1697								priority: 123,1698								call: Call::Logger(logger::Call::log(69, 1000)),1699								maybe_periodic: Some((456u64, 10)),1700								origin: root(),1701								_phantom: PhantomData::<u64>::default(),1702							}),1703						]1704					),1705					(1706						2,1707						vec![1708							Some(ScheduledV2 {1709								maybe_id: None,1710								priority: 12,1711								call: Call::Logger(logger::Call::log(96, 100)),1712								maybe_periodic: None,1713								origin: root(),1714								_phantom: PhantomData::<u64>::default(),1715							}),1716							None,1717							Some(ScheduledV2 {1718								maybe_id: Some(b"test".to_vec()),1719								priority: 123,1720								call: Call::Logger(logger::Call::log(69, 1000)),1721								maybe_periodic: Some((456u64, 10)),1722								origin: root(),1723								_phantom: PhantomData::<u64>::default(),1724							}),1725						]1726					)1727				]1728			);17291730			assert_eq!(StorageVersion::get(), Releases::V2);1731		});1732	}17331734	#[test]1735	fn test_migrate_origin() {1736		new_test_ext().execute_with(|| {1737			for i in 0..3u64 {1738				let k = i.twox_64_concat();1739				let old: Vec<Option<Scheduled<_, _, u32, u64>>> = vec![1740					Some(Scheduled {1741						maybe_id: None,1742						priority: i as u8 + 10,1743						call: Call::Logger(logger::Call::log(96, 100)),1744						origin: 3u32,1745						maybe_periodic: None,1746						_phantom: Default::default(),1747					}),1748					None,1749					Some(Scheduled {1750						maybe_id: Some(b"test".to_vec()),1751						priority: 123,1752						origin: 2u32,1753						call: Call::Logger(logger::Call::log(69, 1000)),1754						maybe_periodic: Some((456u64, 10)),1755						_phantom: Default::default(),1756					}),1757				];1758				frame_support::migration::put_storage_value(b"Scheduler", b"Agenda", &k, old);1759			}17601761			impl From<u32> for OriginCaller {1762				fn from(value: u32) -> Self {1763					match value {1764						3 => system::RawOrigin::Root.into(),1765						2 => system::RawOrigin::None.into(),1766						_ => unimplemented!(),1767					}1768				}1769			}17701771			Scheduler::migrate_origin::<u32>();17721773			assert_eq_uvec!(1774				Agenda::<Test>::iter().collect::<Vec<_>>(),1775				vec![1776					(1777						0,1778						vec![1779							Some(ScheduledV2::<_, _, OriginCaller, u64> {1780								maybe_id: None,1781								priority: 10,1782								call: Call::Logger(logger::Call::log(96, 100)),1783								maybe_periodic: None,1784								origin: system::RawOrigin::Root.into(),1785								_phantom: PhantomData::<u64>::default(),1786							}),1787							None,1788							Some(ScheduledV2 {1789								maybe_id: Some(b"test".to_vec()),1790								priority: 123,1791								call: Call::Logger(logger::Call::log(69, 1000)),1792								maybe_periodic: Some((456u64, 10)),1793								origin: system::RawOrigin::None.into(),1794								_phantom: PhantomData::<u64>::default(),1795							}),1796						]1797					),1798					(1799						1,1800						vec![1801							Some(ScheduledV2 {1802								maybe_id: None,1803								priority: 11,1804								call: Call::Logger(logger::Call::log(96, 100)),1805								maybe_periodic: None,1806								origin: system::RawOrigin::Root.into(),1807								_phantom: PhantomData::<u64>::default(),1808							}),1809							None,1810							Some(ScheduledV2 {1811								maybe_id: Some(b"test".to_vec()),1812								priority: 123,1813								call: Call::Logger(logger::Call::log(69, 1000)),1814								maybe_periodic: Some((456u64, 10)),1815								origin: system::RawOrigin::None.into(),1816								_phantom: PhantomData::<u64>::default(),1817							}),1818						]1819					),1820					(1821						2,1822						vec![1823							Some(ScheduledV2 {1824								maybe_id: None,1825								priority: 12,1826								call: Call::Logger(logger::Call::log(96, 100)),1827								maybe_periodic: None,1828								origin: system::RawOrigin::Root.into(),1829								_phantom: PhantomData::<u64>::default(),1830							}),1831							None,1832							Some(ScheduledV2 {1833								maybe_id: Some(b"test".to_vec()),1834								priority: 123,1835								call: Call::Logger(logger::Call::log(69, 1000)),1836								maybe_periodic: Some((456u64, 10)),1837								origin: system::RawOrigin::None.into(),1838								_phantom: PhantomData::<u64>::default(),1839							}),1840						]1841					)1842				]1843			);1844		});1845	}1846}