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

difftreelog

Merge branch 'feature/internal-audit' of github.com:UniqueNetwork/unique-chain into feature/internal-audit

Greg Zaitsev2021-11-12parents: #4eb90d8 #6aab87c.patch.diff
in: master

2 files changed

modifiedpallets/nft/src/lib.rsdiffbeforeafterboth
--- a/pallets/nft/src/lib.rs
+++ b/pallets/nft/src/lib.rs
@@ -162,7 +162,7 @@
 			0
 		}
 
-		/// This method creates a Collection of NFTs. Each Token may have multiple properties encoded as an array of bytes of certain length. The initial owner and admin of the collection are set to the address that signed the transaction. Both addresses can be changed later.
+		/// This method creates a Collection of NFTs. Each Token may have multiple properties encoded as an array of bytes of certain length. The initial owner of the collection is set to the address that signed the transaction and can be changed later.
 		///
 		/// # Permissions
 		///
modifiedpallets/scheduler/src/lib.rsdiffbeforeafterboth
after · pallets/scheduler/src/lib.rs
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,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;77use scale_info::TypeInfo;7879/// Our pallet's configuration trait. All our types and constants go in here. If the80/// pallet is dependent on specific other pallets, then their configuration traits81/// should be added to our implied traits list.82///83/// `system::Config` should always be included in our implied traits.84/// //85pub trait Config: system::Config {86	/// The overarching event type.87	type Event: From<Event<Self>> + Into<<Self as system::Config>::Event>;8889	/// The aggregated origin which the dispatch will take.90	type Origin: OriginTrait<PalletsOrigin = Self::PalletsOrigin>91		+ From<Self::PalletsOrigin>92		+ IsType<<Self as system::Config>::Origin>;9394	/// The caller origin, overarching type of all pallets origins.95	type PalletsOrigin: From<system::RawOrigin<Self::AccountId>> + Codec + TypeInfo + Clone + Eq;9697	/// The aggregated call type.98	type Call: Parameter99		+ Dispatchable<Origin = <Self as Config>::Origin>100		+ GetDispatchInfo101		+ From<system::Call<Self>>;102103	/// The maximum weight that may be scheduled per block for any dispatchables of less priority104	/// than `schedule::HARD_DEADLINE`.105	type MaximumWeight: Get<Weight>;106107	/// Required origin to schedule or cancel calls.108	type ScheduleOrigin: EnsureOrigin<<Self as system::Config>::Origin>;109110	/// The maximum number of scheduled calls in the queue for a single block.111	/// Not strictly enforced, but used for weight estimation.112	type MaxScheduledPerBlock: Get<u32>;113114	/// Sponsoring function115	type SponsorshipHandler: SponsorshipHandler<Self::AccountId, <Self as Config>::Call>;116117	/// Weight information for extrinsics in this pallet.118	type WeightInfo: WeightInfo;119}120121// pub type SelfWeightInfo<T> = <T as system::Config>::WeightInfo;122123/// Just a simple index for naming period tasks.124pub type PeriodicIndex = u32;125/// The location of a scheduled task that can be used to remove it.126pub type TaskAddress<BlockNumber> = (BlockNumber, u32);127128#[cfg_attr(any(feature = "std", test), derive(PartialEq, Eq))]129#[derive(Clone, RuntimeDebug, Encode, Decode)]130struct ScheduledV1<Call, BlockNumber> {131	maybe_id: Option<Vec<u8>>,132	priority: schedule::Priority,133	call: Call,134	maybe_periodic: Option<schedule::Period<BlockNumber>>,135}136137/// Information regarding an item to be executed in the future.138#[cfg_attr(any(feature = "std", test), derive(PartialEq, Eq))]139#[derive(Clone, RuntimeDebug, Encode, Decode, TypeInfo)]140pub struct ScheduledV2<Call, BlockNumber, PalletsOrigin, AccountId> {141	/// The unique identity for this task, if there is one.142	maybe_id: Option<Vec<u8>>,143	/// This task's priority.144	priority: schedule::Priority,145	/// The call to be dispatched.146	call: Call,147	/// If the call is periodic, then this points to the information concerning that.148	maybe_periodic: Option<schedule::Period<BlockNumber>>,149	/// The origin to dispatch the call.150	origin: PalletsOrigin,151	_phantom: PhantomData<AccountId>,152}153154/// The current version of Scheduled struct.155pub type Scheduled<Call, BlockNumber, PalletsOrigin, AccountId> =156	ScheduledV2<Call, BlockNumber, PalletsOrigin, AccountId>;157158// A value placed in storage that represents the current version of the Scheduler storage.159// This value is used by the `on_runtime_upgrade` logic to determine whether we run160// storage migration logic.161#[derive(Encode, Decode, Clone, Copy, PartialEq, Eq, RuntimeDebug, TypeInfo)]162enum Releases {163	V1,164	V2,165}166167impl Default for Releases {168	fn default() -> Self {169		Releases::V1170	}171}172173#[derive(Encode, Decode, Clone, PartialEq, Eq, RuntimeDebug, TypeInfo)]174pub struct CallSpec {175	module: u32,176	method: u32,177}178179decl_storage! {180	trait Store for Module<T: Config> as Scheduler {181		/// Items to be executed, indexed by the block number that they should be executed on.182		pub Agenda: map hasher(twox_64_concat) T::BlockNumber183			=> Vec<Option<Scheduled<<T as Config>::Call, T::BlockNumber, T::PalletsOrigin, T::AccountId>>>;184185		pub SpecAgenda: map hasher(twox_64_concat) T::BlockNumber186			=> Vec<Option<CallSpec>>;187188		/// Lookup from identity to the block number and index of the task.189		Lookup: map hasher(twox_64_concat) Vec<u8> => Option<TaskAddress<T::BlockNumber>>;190191		/// Storage version of the pallet.192		///193		/// New networks start with last version.194		StorageVersion build(|_| Releases::V2): Releases;195	}196}197198decl_event!(199	pub enum Event<T> where <T as system::Config>::BlockNumber {200		/// Scheduled some task. \[when, index\]201		Scheduled(BlockNumber, u32),202		/// Canceled some task. \[when, index\]203		Canceled(BlockNumber, u32),204		/// Dispatched some task. \[task, id, result\]205		Dispatched(TaskAddress<BlockNumber>, Option<Vec<u8>>, DispatchResult),206	}207);208209decl_error! {210	pub enum Error for Module<T: Config> {211		/// Failed to schedule a call212		FailedToSchedule,213		/// Cannot find the scheduled call.214		NotFound,215		/// Given target block number is in the past.216		TargetBlockNumberInPast,217		/// Reschedule failed because it does not change scheduled time.218		RescheduleNoChange,219	}220}221222decl_module! {223	/// Scheduler module declaration.224	pub struct Module<T: Config> for enum Call225	where226		origin: <T as system::Config>::Origin227	{228		type Error = Error<T>;229		fn deposit_event() = default;230231232		/// Anonymously schedule a task.233		///234		/// # <weight>235		/// - S = Number of already scheduled calls236		/// - Base Weight: 22.29 + .126 * S µs237		/// - DB Weight:238		///     - Read: Agenda239		///     - Write: Agenda240		/// - Will use base weight of 25 which should be good for up to 30 scheduled calls241		/// # </weight>242		#[weight = <T as Config>::WeightInfo::schedule(T::MaxScheduledPerBlock::get())]243		fn schedule(origin,244			when: T::BlockNumber,245			maybe_periodic: Option<schedule::Period<T::BlockNumber>>,246			priority: schedule::Priority,247			call: Box<<T as Config>::Call>,248		)249		{250			let origin = <T as Config>::Origin::from(origin);251			Self::do_schedule(DispatchTime::At(when), maybe_periodic, priority, origin.caller().clone(), *call)?;252		}253254		/// Cancel an anonymously scheduled task.255		///256		/// # <weight>257		/// - S = Number of already scheduled calls258		/// - Base Weight: 22.15 + 2.869 * S µs259		/// - DB Weight:260		///     - Read: Agenda261		///     - Write: Agenda, Lookup262		/// - Will use base weight of 100 which should be good for up to 30 scheduled calls263		/// # </weight>264		#[weight = <T as Config>::WeightInfo::cancel(T::MaxScheduledPerBlock::get())]265		fn cancel(origin, when: T::BlockNumber, index: u32) {266			T::ScheduleOrigin::ensure_origin(origin.clone())?;267			let origin = <T as Config>::Origin::from(origin);268			Self::do_cancel(Some(origin.caller().clone()), (when, index))?;269		}270271		/// Schedule a named task.272		///273		/// # <weight>274		/// - S = Number of already scheduled calls275		/// - Base Weight: 29.6 + .159 * S µs276		/// - DB Weight:277		///     - Read: Agenda, Lookup278		///     - Write: Agenda, Lookup279		/// - Will use base weight of 35 which should be good for more than 30 scheduled calls280		/// # </weight>281		#[weight = <T as Config>::WeightInfo::schedule_named(T::MaxScheduledPerBlock::get())]282		fn schedule_named(origin,283			id: Vec<u8>,284			when: T::BlockNumber,285			maybe_periodic: Option<schedule::Period<T::BlockNumber>>,286			priority: schedule::Priority,287			call: Box<<T as Config>::Call>,288		) {289			T::ScheduleOrigin::ensure_origin(origin.clone())?;290			let origin = <T as Config>::Origin::from(origin);291			Self::do_schedule_named(292				id, DispatchTime::At(when), maybe_periodic, priority, origin.caller().clone(), *call293			)?;294		}295296		/// Cancel a named scheduled task.297		///298		/// # <weight>299		/// - S = Number of already scheduled calls300		/// - Base Weight: 24.91 + 2.907 * S µs301		/// - DB Weight:302		///     - Read: Agenda, Lookup303		///     - Write: Agenda, Lookup304		/// - Will use base weight of 100 which should be good for up to 30 scheduled calls305		/// # </weight>306		#[weight = <T as Config>::WeightInfo::cancel_named(T::MaxScheduledPerBlock::get())]307		fn cancel_named(origin, id: Vec<u8>) {308			T::ScheduleOrigin::ensure_origin(origin.clone())?;309			let origin = <T as Config>::Origin::from(origin);310			Self::do_cancel_named(Some(origin.caller().clone()), id)?;311		}312313		/// Anonymously schedule a task after a delay.314		///315		/// # <weight>316		/// Same as [`schedule`].317		/// # </weight>318		#[weight = <T as Config>::WeightInfo::schedule(T::MaxScheduledPerBlock::get())]319		fn schedule_after(origin,320			after: T::BlockNumber,321			maybe_periodic: Option<schedule::Period<T::BlockNumber>>,322			priority: schedule::Priority,323			call: Box<<T as Config>::Call>,324		) {325			T::ScheduleOrigin::ensure_origin(origin.clone())?;326			let origin = <T as Config>::Origin::from(origin);327			Self::do_schedule(328				DispatchTime::After(after), maybe_periodic, priority, origin.caller().clone(), *call329			)?;330		}331332		/// Schedule a named task after a delay.333		///334		/// # <weight>335		/// Same as [`schedule_named`].336		/// # </weight>337		#[weight = <T as Config>::WeightInfo::schedule_named(T::MaxScheduledPerBlock::get())]338		fn schedule_named_after(origin,339			id: Vec<u8>,340			after: T::BlockNumber,341			maybe_periodic: Option<schedule::Period<T::BlockNumber>>,342			priority: schedule::Priority,343			call: Box<<T as Config>::Call>,344		) {345			T::ScheduleOrigin::ensure_origin(origin.clone())?;346			let origin = <T as Config>::Origin::from(origin);347			Self::do_schedule_named(348				id, DispatchTime::After(after), maybe_periodic, priority, origin.caller().clone(), *call349			)?;350		}351352		/// Execute the scheduled calls353		///354		/// # <weight>355		/// - S = Number of already scheduled calls356		/// - N = Named scheduled calls357		/// - P = Periodic Calls358		/// - Base Weight: 9.243 + 23.45 * S µs359		/// - DB Weight:360		///     - Read: Agenda + Lookup * N + Agenda(Future) * P361		///     - Write: Agenda + Lookup * N  + Agenda(future) * P362		/// # </weight>363		fn on_initialize(now: T::BlockNumber) -> Weight {364			let limit = T::MaximumWeight::get();365			let mut queued = Agenda::<T>::take(now).into_iter()366				.enumerate()367				.filter_map(|(index, s)| s.map(|inner| (index as u32, inner)))368				.collect::<Vec<_>>();369			if queued.len() as u32 > T::MaxScheduledPerBlock::get() {370				log::warn!(371					target: "runtime::scheduler",372					"Warning: This block has more items queued in Scheduler than \373					expected from the runtime configuration. An update might be needed."374				);375			}376			queued.sort_by_key(|(_, s)| s.priority);377			let base_weight: Weight = T::DbWeight::get().reads_writes(1, 2); // Agenda + Agenda(next)378			let mut total_weight: Weight = 0;379			queued.into_iter()380				.enumerate()381				.scan(base_weight, |cumulative_weight, (order, (index, s))| {382					*cumulative_weight = cumulative_weight383						.saturating_add(s.call.get_dispatch_info().weight);384385					let origin = <<T as Config>::Origin as From<T::PalletsOrigin>>::from(386						s.origin.clone()387					).into();388389					if ensure_signed(origin).is_ok() {390						 // AccountData for inner call origin accountdata.391						*cumulative_weight = cumulative_weight392							.saturating_add(T::DbWeight::get().reads_writes(1, 1));393					}394395					if s.maybe_id.is_some() {396						// Remove/Modify Lookup397						*cumulative_weight = cumulative_weight.saturating_add(T::DbWeight::get().writes(1));398					}399					if s.maybe_periodic.is_some() {400						// Read/Write Agenda for future block401						*cumulative_weight = cumulative_weight.saturating_add(T::DbWeight::get().reads_writes(1, 1));402					}403404					Some((order, index, *cumulative_weight, s))405				})406				.filter_map(|(order, index, cumulative_weight, mut s)| {407					// We allow a scheduled call if any is true:408					// - It's priority is `HARD_DEADLINE`409					// - It does not push the weight past the limit.410					// - It is the first item in the schedule411					if s.priority <= schedule::HARD_DEADLINE || cumulative_weight <= limit || order == 0 {412413						let origin = <<T as Config>::Origin as From<T::PalletsOrigin>>::from(414							s.origin.clone()415						).into();416						let sender = ensure_signed(origin).unwrap_or_default();417						let who_will_pay = T::SponsorshipHandler::get_sponsor(&sender, &s.call).unwrap_or(sender);418						let sponsor = T::PalletsOrigin::from(system::RawOrigin::Signed(who_will_pay));419						let r = s.call.clone().dispatch(sponsor.into());420						let maybe_id = s.maybe_id.clone();421						if let Some((period, count)) = s.maybe_periodic {422							if count > 1 {423								s.maybe_periodic = Some((period, count - 1));424							} else {425								s.maybe_periodic = None;426							}427							let next = now + period;428							// If scheduled is named, place it's information in `Lookup`429							if let Some(ref id) = s.maybe_id {430								let next_index = Agenda::<T>::decode_len(now + period).unwrap_or(0);431								Lookup::<T>::insert(id, (next, next_index as u32));432							}433							Agenda::<T>::append(next, Some(s));434						} else if let Some(ref id) = s.maybe_id {435									  Lookup::<T>::remove(id);436								  }437						Self::deposit_event(RawEvent::Dispatched(438							(now, index),439							maybe_id,440							r.map(|_| ()).map_err(|e| e.error)441						));442						total_weight = cumulative_weight;443						None444					} else {445						Some(Some(s))446					}447				})448				.for_each(|unused| {449					let next = now + One::one();450					Agenda::<T>::append(next, unused);451				});452453			total_weight454		}455	}456}457458impl<T: Config> Module<T> {459	fn resolve_time(when: DispatchTime<T::BlockNumber>) -> Result<T::BlockNumber, DispatchError> {460		let now = frame_system::Pallet::<T>::block_number();461462		let when = match when {463			DispatchTime::At(x) => x,464			// The current block has already completed it's scheduled tasks, so465			// Schedule the task at lest one block after this current block.466			DispatchTime::After(x) => now.saturating_add(x).saturating_add(One::one()),467		};468469		if when <= now {470			return Err(Error::<T>::TargetBlockNumberInPast.into());471		}472473		Ok(when)474	}475476	fn do_schedule(477		when: DispatchTime<T::BlockNumber>,478		maybe_periodic: Option<schedule::Period<T::BlockNumber>>,479		priority: schedule::Priority,480		origin: T::PalletsOrigin,481		call: <T as Config>::Call,482	) -> Result<TaskAddress<T::BlockNumber>, DispatchError> {483		let when = Self::resolve_time(when)?;484485		// sanitize maybe_periodic486		let maybe_periodic = maybe_periodic487			.filter(|p| p.1 > 1 && !p.0.is_zero())488			// Remove one from the number of repetitions since we will schedule one now.489			.map(|(p, c)| (p, c - 1));490		let s = Some(Scheduled {491			maybe_id: None,492			priority,493			call,494			maybe_periodic,495			origin,496			_phantom: PhantomData::<T::AccountId>::default(),497		});498		Agenda::<T>::append(when, s);499		let index = Agenda::<T>::decode_len(when).unwrap_or(1) as u32 - 1;500		if index > T::MaxScheduledPerBlock::get() {501			log::warn!(502				target: "runtime::scheduler",503				"Warning: There are more items queued in the Scheduler than \504				expected from the runtime configuration. An update might be needed.",505			);506		}507		Self::deposit_event(RawEvent::Scheduled(when, index));508509		Ok((when, index))510	}511512	fn do_cancel(513		origin: Option<T::PalletsOrigin>,514		(when, index): TaskAddress<T::BlockNumber>,515	) -> Result<(), DispatchError> {516		let scheduled = Agenda::<T>::try_mutate(when, |agenda| {517			agenda.get_mut(index as usize).map_or(518				Ok(None),519				|s| -> Result<Option<Scheduled<_, _, _, _>>, DispatchError> {520					if let (Some(ref o), Some(ref s)) = (origin, s.borrow()) {521						if *o != s.origin {522							return Err(BadOrigin.into());523						}524					};525					Ok(s.take())526				},527			)528		})?;529		if let Some(s) = scheduled {530			if let Some(id) = s.maybe_id {531				Lookup::<T>::remove(id);532			}533			Self::deposit_event(RawEvent::Canceled(when, index));534			Ok(())535		} else {536			Err(Error::<T>::NotFound.into())537		}538	}539540	fn do_reschedule(541		(when, index): TaskAddress<T::BlockNumber>,542		new_time: DispatchTime<T::BlockNumber>,543	) -> Result<TaskAddress<T::BlockNumber>, DispatchError> {544		let new_time = Self::resolve_time(new_time)?;545546		if new_time == when {547			return Err(Error::<T>::RescheduleNoChange.into());548		}549550		Agenda::<T>::try_mutate(when, |agenda| -> DispatchResult {551			let task = agenda.get_mut(index as usize).ok_or(Error::<T>::NotFound)?;552			let task = task.take().ok_or(Error::<T>::NotFound)?;553			Agenda::<T>::append(new_time, Some(task));554			Ok(())555		})?;556557		let new_index = Agenda::<T>::decode_len(new_time).unwrap_or(1) as u32 - 1;558		Self::deposit_event(RawEvent::Canceled(when, index));559		Self::deposit_event(RawEvent::Scheduled(new_time, new_index));560561		Ok((new_time, new_index))562	}563564	fn do_schedule_named(565		id: Vec<u8>,566		when: DispatchTime<T::BlockNumber>,567		maybe_periodic: Option<schedule::Period<T::BlockNumber>>,568		priority: schedule::Priority,569		origin: T::PalletsOrigin,570		call: <T as Config>::Call,571	) -> Result<TaskAddress<T::BlockNumber>, DispatchError> {572		// ensure id it is unique573		if Lookup::<T>::contains_key(&id) {574			return Err(Error::<T>::FailedToSchedule.into());575		}576577		let when = Self::resolve_time(when)?;578579		// sanitize maybe_periodic580		let maybe_periodic = maybe_periodic581			.filter(|p| p.1 > 1 && !p.0.is_zero())582			// Remove one from the number of repetitions since we will schedule one now.583			.map(|(p, c)| (p, c - 1));584585		let s = Scheduled {586			maybe_id: Some(id.clone()),587			priority,588			call,589			maybe_periodic,590			origin,591			_phantom: Default::default(),592		};593		Agenda::<T>::append(when, Some(s));594		let index = Agenda::<T>::decode_len(when).unwrap_or(1) as u32 - 1;595		if index > T::MaxScheduledPerBlock::get() {596			log::warn!(597				target: "runtime::scheduler",598				"Warning: There are more items queued in the Scheduler than \599				expected from the runtime configuration. An update might be needed.",600			);601		}602		let address = (when, index);603		Lookup::<T>::insert(&id, &address);604		Self::deposit_event(RawEvent::Scheduled(when, index));605606		Ok(address)607	}608609	fn do_cancel_named(origin: Option<T::PalletsOrigin>, id: Vec<u8>) -> DispatchResult {610		Lookup::<T>::try_mutate_exists(id, |lookup| -> DispatchResult {611			if let Some((when, index)) = lookup.take() {612				let i = index as usize;613				Agenda::<T>::try_mutate(when, |agenda| -> DispatchResult {614					if let Some(s) = agenda.get_mut(i) {615						if let (Some(ref o), Some(ref s)) = (origin, s.borrow()) {616							if *o != s.origin {617								return Err(BadOrigin.into());618							}619						}620						*s = None;621					}622					Ok(())623				})?;624				Self::deposit_event(RawEvent::Canceled(when, index));625				Ok(())626			} else {627				Err(Error::<T>::NotFound.into())628			}629		})630	}631632	fn do_reschedule_named(633		id: Vec<u8>,634		new_time: DispatchTime<T::BlockNumber>,635	) -> Result<TaskAddress<T::BlockNumber>, DispatchError> {636		let new_time = Self::resolve_time(new_time)?;637638		Lookup::<T>::try_mutate_exists(639			id,640			|lookup| -> Result<TaskAddress<T::BlockNumber>, DispatchError> {641				let (when, index) = lookup.ok_or(Error::<T>::NotFound)?;642643				if new_time == when {644					return Err(Error::<T>::RescheduleNoChange.into());645				}646647				Agenda::<T>::try_mutate(when, |agenda| -> DispatchResult {648					let task = agenda.get_mut(index as usize).ok_or(Error::<T>::NotFound)?;649					let task = task.take().ok_or(Error::<T>::NotFound)?;650					Agenda::<T>::append(new_time, Some(task));651652					Ok(())653				})?;654655				let new_index = Agenda::<T>::decode_len(new_time).unwrap_or(1) as u32 - 1;656				Self::deposit_event(RawEvent::Canceled(when, index));657				Self::deposit_event(RawEvent::Scheduled(new_time, new_index));658659				*lookup = Some((new_time, new_index));660661				Ok((new_time, new_index))662			},663		)664	}665}666667impl<T: Config> schedule::Anon<T::BlockNumber, <T as Config>::Call, T::PalletsOrigin>668	for Module<T>669{670	type Address = TaskAddress<T::BlockNumber>;671672	fn schedule(673		when: DispatchTime<T::BlockNumber>,674		maybe_periodic: Option<schedule::Period<T::BlockNumber>>,675		priority: schedule::Priority,676		origin: T::PalletsOrigin,677		call: <T as Config>::Call,678	) -> Result<Self::Address, DispatchError> {679		Self::do_schedule(when, maybe_periodic, priority, origin, call)680	}681682	fn cancel((when, index): Self::Address) -> Result<(), ()> {683		Self::do_cancel(None, (when, index)).map_err(|_| ())684	}685686	fn reschedule(687		address: Self::Address,688		when: DispatchTime<T::BlockNumber>,689	) -> Result<Self::Address, DispatchError> {690		Self::do_reschedule(address, when)691	}692693	fn next_dispatch_time((when, index): Self::Address) -> Result<T::BlockNumber, ()> {694		Agenda::<T>::get(when)695			.get(index as usize)696			.ok_or(())697			.map(|_| when)698	}699}700701impl<T: Config> schedule::Named<T::BlockNumber, <T as Config>::Call, T::PalletsOrigin>702	for Module<T>703{704	type Address = TaskAddress<T::BlockNumber>;705706	fn schedule_named(707		id: Vec<u8>,708		when: DispatchTime<T::BlockNumber>,709		maybe_periodic: Option<schedule::Period<T::BlockNumber>>,710		priority: schedule::Priority,711		origin: T::PalletsOrigin,712		call: <T as Config>::Call,713	) -> Result<Self::Address, ()> {714		Self::do_schedule_named(id, when, maybe_periodic, priority, origin, call).map_err(|_| ())715	}716717	fn cancel_named(id: Vec<u8>) -> Result<(), ()> {718		Self::do_cancel_named(None, id).map_err(|_| ())719	}720721	fn reschedule_named(722		id: Vec<u8>,723		when: DispatchTime<T::BlockNumber>,724	) -> Result<Self::Address, DispatchError> {725		Self::do_reschedule_named(id, when)726	}727728	fn next_dispatch_time(id: Vec<u8>) -> Result<T::BlockNumber, ()> {729		Lookup::<T>::get(id)730			.and_then(|(when, index)| Agenda::<T>::get(when).get(index as usize).map(|_| when))731			.ok_or(())732	}733}734735#[cfg(test)]736#[allow(clippy::from_over_into)]737mod tests {738	use super::*;739740	use frame_support::{741		Hashable, assert_err, assert_noop, assert_ok, ord_parameter_types, parameter_types,742		traits::{Contains, OnFinalize, OnInitialize},743		weights::constants::RocksDbWeight,744	};745	use sp_core::H256;746	use sp_runtime::{747		Perbill,748		testing::Header,749		traits::{BlakeTwo256, IdentityLookup},750	};751	use frame_system::{EnsureOneOf, EnsureRoot, EnsureSignedBy};752	use substrate_test_utils::assert_eq_uvec;753	use crate as scheduler;754755	mod logger {756		use super::*;757		use std::cell::RefCell;758759		thread_local! {760			static LOG: RefCell<Vec<(OriginCaller, u32)>> = RefCell::new(Vec::new());761		}762		pub fn log() -> Vec<(OriginCaller, u32)> {763			LOG.with(|log| log.borrow().clone())764		}765		pub trait Config: system::Config {766			type Event: From<Event> + Into<<Self as system::Config>::Event>;767		}768		decl_event! {769			pub enum Event {770				Logged(u32, Weight),771			}772		}773		decl_module! {774			pub struct Module<T: Config> for enum Call775			where776				origin: <T as system::Config>::Origin,777				<T as system::Config>::Origin: OriginTrait<PalletsOrigin = OriginCaller>778			{779				fn deposit_event() = default;780781				#[weight = *weight]782				fn log(origin, i: u32, weight: Weight) {783					Self::deposit_event(Event::Logged(i, weight));784					LOG.with(|log| {785						log.borrow_mut().push((origin.caller().clone(), i));786					})787				}788789				#[weight = *weight]790				fn log_without_filter(origin, i: u32, weight: Weight) {791					Self::deposit_event(Event::Logged(i, weight));792					LOG.with(|log| {793						log.borrow_mut().push((origin.caller().clone(), i));794					})795				}796			}797		}798	}799800	type UncheckedExtrinsic = frame_system::mocking::MockUncheckedExtrinsic<Test>;801	type Block = frame_system::mocking::MockBlock<Test>;802803	frame_support::construct_runtime!(804		pub enum Test where805			Block = Block,806			NodeBlock = Block,807			UncheckedExtrinsic = UncheckedExtrinsic,808		{809			System: frame_system::{Pallet, Call, Config, Storage, Event<T>},810			Logger: logger::{Pallet, Call, Event},811			Scheduler: scheduler::{Pallet, Call, Storage, Event<T>},812		}813	);814815	// Scheduler must dispatch with root and no filter, this tests base filter is indeed not used.816	pub struct BaseFilter;817	impl Contains<Call> for BaseFilter {818		fn contains(call: &Call) -> bool {819			!matches!(call, Call::Logger(logger::Call::log { .. }))820		}821	}822823	parameter_types! {824		pub const BlockHashCount: u64 = 250;825		pub BlockWeights: frame_system::limits::BlockWeights =826			frame_system::limits::BlockWeights::simple_max(2_000_000_000_000);827	}828	impl system::Config for Test {829		type BaseCallFilter = BaseFilter;830		type BlockWeights = ();831		type BlockLength = ();832		type DbWeight = RocksDbWeight;833		type Origin = Origin;834		type Call = Call;835		type Index = u64;836		type BlockNumber = u64;837		type Hash = H256;838		type Hashing = BlakeTwo256;839		type AccountId = u64;840		type Lookup = IdentityLookup<Self::AccountId>;841		type Header = Header;842		type Event = Event;843		type BlockHashCount = BlockHashCount;844		type Version = ();845		type PalletInfo = PalletInfo;846		type AccountData = ();847		type OnNewAccount = ();848		type OnKilledAccount = ();849		type SystemWeightInfo = ();850		type SS58Prefix = ();851		type OnSetCode = ();852	}853	impl logger::Config for Test {854		type Event = Event;855	}856	parameter_types! {857		pub MaximumSchedulerWeight: Weight = Perbill::from_percent(80) * BlockWeights::get().max_block;858		pub const MaxScheduledPerBlock: u32 = 10;859	}860	ord_parameter_types! {861		pub const One: u64 = 1;862	}863864	impl Config for Test {865		type Event = Event;866		type Origin = Origin;867		type PalletsOrigin = OriginCaller;868		type Call = Call;869		type MaximumWeight = MaximumSchedulerWeight;870		type ScheduleOrigin = EnsureOneOf<u64, EnsureRoot<u64>, EnsureSignedBy<One, u64>>;871		type MaxScheduledPerBlock = MaxScheduledPerBlock;872		type WeightInfo = ();873		type SponsorshipHandler = ();874	}875876	pub fn new_test_ext() -> sp_io::TestExternalities {877		let t = system::GenesisConfig::default()878			.build_storage::<Test>()879			.unwrap();880		t.into()881	}882883	fn run_to_block(n: u64) {884		while System::block_number() < n {885			Scheduler::on_finalize(System::block_number());886			System::set_block_number(System::block_number() + 1);887			Scheduler::on_initialize(System::block_number());888		}889	}890891	fn root() -> OriginCaller {892		system::RawOrigin::Root.into()893	}894}