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
before · 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, 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;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	/// Migrate storage format from V1 to V2.460	/// Return true if migration is performed.461	pub fn migrate_v1_to_t2() -> bool {462		if StorageVersion::get() == Releases::V1 {463			StorageVersion::put(Releases::V2);464465			Agenda::<T>::translate::<466				Vec<Option<ScheduledV1<<T as Config>::Call, T::BlockNumber>>>,467				_,468			>(|_, agenda| {469				Some(470					agenda471						.into_iter()472						.map(|schedule| {473							schedule.map(|schedule| ScheduledV2 {474								maybe_id: schedule.maybe_id,475								priority: schedule.priority,476								call: schedule.call,477								maybe_periodic: schedule.maybe_periodic,478								origin: system::RawOrigin::Root.into(),479								_phantom: Default::default(),480							})481						})482						.collect::<Vec<_>>(),483				)484			});485486			true487		} else {488			false489		}490	}491492	/// Helper to migrate scheduler when the pallet origin type has changed.493	pub fn migrate_origin<OldOrigin: Into<T::PalletsOrigin> + codec::Decode>() {494		Agenda::<T>::translate::<495			Vec<Option<Scheduled<<T as Config>::Call, T::BlockNumber, OldOrigin, T::AccountId>>>,496			_,497		>(|_, agenda| {498			Some(499				agenda500					.into_iter()501					.map(|schedule| {502						schedule.map(|schedule| Scheduled {503							maybe_id: schedule.maybe_id,504							priority: schedule.priority,505							call: schedule.call,506							maybe_periodic: schedule.maybe_periodic,507							origin: schedule.origin.into(),508							_phantom: Default::default(),509						})510					})511					.collect::<Vec<_>>(),512			)513		});514	}515516	fn resolve_time(when: DispatchTime<T::BlockNumber>) -> Result<T::BlockNumber, DispatchError> {517		let now = frame_system::Pallet::<T>::block_number();518519		let when = match when {520			DispatchTime::At(x) => x,521			// The current block has already completed it's scheduled tasks, so522			// Schedule the task at lest one block after this current block.523			DispatchTime::After(x) => now.saturating_add(x).saturating_add(One::one()),524		};525526		if when <= now {527			return Err(Error::<T>::TargetBlockNumberInPast.into());528		}529530		Ok(when)531	}532533	fn do_schedule(534		when: DispatchTime<T::BlockNumber>,535		maybe_periodic: Option<schedule::Period<T::BlockNumber>>,536		priority: schedule::Priority,537		origin: T::PalletsOrigin,538		call: <T as Config>::Call,539	) -> Result<TaskAddress<T::BlockNumber>, DispatchError> {540		let when = Self::resolve_time(when)?;541542		// sanitize maybe_periodic543		let maybe_periodic = maybe_periodic544			.filter(|p| p.1 > 1 && !p.0.is_zero())545			// Remove one from the number of repetitions since we will schedule one now.546			.map(|(p, c)| (p, c - 1));547		let s = Some(Scheduled {548			maybe_id: None,549			priority,550			call,551			maybe_periodic,552			origin,553			_phantom: PhantomData::<T::AccountId>::default(),554		});555		Agenda::<T>::append(when, s);556		let index = Agenda::<T>::decode_len(when).unwrap_or(1) as u32 - 1;557		if index > T::MaxScheduledPerBlock::get() {558			log::warn!(559				target: "runtime::scheduler",560				"Warning: There are more items queued in the Scheduler than \561				expected from the runtime configuration. An update might be needed.",562			);563		}564		Self::deposit_event(RawEvent::Scheduled(when, index));565566		Ok((when, index))567	}568569	fn do_cancel(570		origin: Option<T::PalletsOrigin>,571		(when, index): TaskAddress<T::BlockNumber>,572	) -> Result<(), DispatchError> {573		let scheduled = Agenda::<T>::try_mutate(when, |agenda| {574			agenda.get_mut(index as usize).map_or(575				Ok(None),576				|s| -> Result<Option<Scheduled<_, _, _, _>>, DispatchError> {577					if let (Some(ref o), Some(ref s)) = (origin, s.borrow()) {578						if *o != s.origin {579							return Err(BadOrigin.into());580						}581					};582					Ok(s.take())583				},584			)585		})?;586		if let Some(s) = scheduled {587			if let Some(id) = s.maybe_id {588				Lookup::<T>::remove(id);589			}590			Self::deposit_event(RawEvent::Canceled(when, index));591			Ok(())592		} else {593			Err(Error::<T>::NotFound.into())594		}595	}596597	fn do_reschedule(598		(when, index): TaskAddress<T::BlockNumber>,599		new_time: DispatchTime<T::BlockNumber>,600	) -> Result<TaskAddress<T::BlockNumber>, DispatchError> {601		let new_time = Self::resolve_time(new_time)?;602603		if new_time == when {604			return Err(Error::<T>::RescheduleNoChange.into());605		}606607		Agenda::<T>::try_mutate(when, |agenda| -> DispatchResult {608			let task = agenda.get_mut(index as usize).ok_or(Error::<T>::NotFound)?;609			let task = task.take().ok_or(Error::<T>::NotFound)?;610			Agenda::<T>::append(new_time, Some(task));611			Ok(())612		})?;613614		let new_index = Agenda::<T>::decode_len(new_time).unwrap_or(1) as u32 - 1;615		Self::deposit_event(RawEvent::Canceled(when, index));616		Self::deposit_event(RawEvent::Scheduled(new_time, new_index));617618		Ok((new_time, new_index))619	}620621	fn do_schedule_named(622		id: Vec<u8>,623		when: DispatchTime<T::BlockNumber>,624		maybe_periodic: Option<schedule::Period<T::BlockNumber>>,625		priority: schedule::Priority,626		origin: T::PalletsOrigin,627		call: <T as Config>::Call,628	) -> Result<TaskAddress<T::BlockNumber>, DispatchError> {629		// ensure id it is unique630		if Lookup::<T>::contains_key(&id) {631			return Err(Error::<T>::FailedToSchedule.into());632		}633634		let when = Self::resolve_time(when)?;635636		// sanitize maybe_periodic637		let maybe_periodic = maybe_periodic638			.filter(|p| p.1 > 1 && !p.0.is_zero())639			// Remove one from the number of repetitions since we will schedule one now.640			.map(|(p, c)| (p, c - 1));641642		let s = Scheduled {643			maybe_id: Some(id.clone()),644			priority,645			call,646			maybe_periodic,647			origin,648			_phantom: Default::default(),649		};650		Agenda::<T>::append(when, Some(s));651		let index = Agenda::<T>::decode_len(when).unwrap_or(1) as u32 - 1;652		if index > T::MaxScheduledPerBlock::get() {653			log::warn!(654				target: "runtime::scheduler",655				"Warning: There are more items queued in the Scheduler than \656				expected from the runtime configuration. An update might be needed.",657			);658		}659		let address = (when, index);660		Lookup::<T>::insert(&id, &address);661		Self::deposit_event(RawEvent::Scheduled(when, index));662663		Ok(address)664	}665666	fn do_cancel_named(origin: Option<T::PalletsOrigin>, id: Vec<u8>) -> DispatchResult {667		Lookup::<T>::try_mutate_exists(id, |lookup| -> DispatchResult {668			if let Some((when, index)) = lookup.take() {669				let i = index as usize;670				Agenda::<T>::try_mutate(when, |agenda| -> DispatchResult {671					if let Some(s) = agenda.get_mut(i) {672						if let (Some(ref o), Some(ref s)) = (origin, s.borrow()) {673							if *o != s.origin {674								return Err(BadOrigin.into());675							}676						}677						*s = None;678					}679					Ok(())680				})?;681				Self::deposit_event(RawEvent::Canceled(when, index));682				Ok(())683			} else {684				Err(Error::<T>::NotFound.into())685			}686		})687	}688689	fn do_reschedule_named(690		id: Vec<u8>,691		new_time: DispatchTime<T::BlockNumber>,692	) -> Result<TaskAddress<T::BlockNumber>, DispatchError> {693		let new_time = Self::resolve_time(new_time)?;694695		Lookup::<T>::try_mutate_exists(696			id,697			|lookup| -> Result<TaskAddress<T::BlockNumber>, DispatchError> {698				let (when, index) = lookup.ok_or(Error::<T>::NotFound)?;699700				if new_time == when {701					return Err(Error::<T>::RescheduleNoChange.into());702				}703704				Agenda::<T>::try_mutate(when, |agenda| -> DispatchResult {705					let task = agenda.get_mut(index as usize).ok_or(Error::<T>::NotFound)?;706					let task = task.take().ok_or(Error::<T>::NotFound)?;707					Agenda::<T>::append(new_time, Some(task));708709					Ok(())710				})?;711712				let new_index = Agenda::<T>::decode_len(new_time).unwrap_or(1) as u32 - 1;713				Self::deposit_event(RawEvent::Canceled(when, index));714				Self::deposit_event(RawEvent::Scheduled(new_time, new_index));715716				*lookup = Some((new_time, new_index));717718				Ok((new_time, new_index))719			},720		)721	}722}723724impl<T: Config> schedule::Anon<T::BlockNumber, <T as Config>::Call, T::PalletsOrigin>725	for Module<T>726{727	type Address = TaskAddress<T::BlockNumber>;728729	fn schedule(730		when: DispatchTime<T::BlockNumber>,731		maybe_periodic: Option<schedule::Period<T::BlockNumber>>,732		priority: schedule::Priority,733		origin: T::PalletsOrigin,734		call: <T as Config>::Call,735	) -> Result<Self::Address, DispatchError> {736		Self::do_schedule(when, maybe_periodic, priority, origin, call)737	}738739	fn cancel((when, index): Self::Address) -> Result<(), ()> {740		Self::do_cancel(None, (when, index)).map_err(|_| ())741	}742743	fn reschedule(744		address: Self::Address,745		when: DispatchTime<T::BlockNumber>,746	) -> Result<Self::Address, DispatchError> {747		Self::do_reschedule(address, when)748	}749750	fn next_dispatch_time((when, index): Self::Address) -> Result<T::BlockNumber, ()> {751		Agenda::<T>::get(when)752			.get(index as usize)753			.ok_or(())754			.map(|_| when)755	}756}757758impl<T: Config> schedule::Named<T::BlockNumber, <T as Config>::Call, T::PalletsOrigin>759	for Module<T>760{761	type Address = TaskAddress<T::BlockNumber>;762763	fn schedule_named(764		id: Vec<u8>,765		when: DispatchTime<T::BlockNumber>,766		maybe_periodic: Option<schedule::Period<T::BlockNumber>>,767		priority: schedule::Priority,768		origin: T::PalletsOrigin,769		call: <T as Config>::Call,770	) -> Result<Self::Address, ()> {771		Self::do_schedule_named(id, when, maybe_periodic, priority, origin, call).map_err(|_| ())772	}773774	fn cancel_named(id: Vec<u8>) -> Result<(), ()> {775		Self::do_cancel_named(None, id).map_err(|_| ())776	}777778	fn reschedule_named(779		id: Vec<u8>,780		when: DispatchTime<T::BlockNumber>,781	) -> Result<Self::Address, DispatchError> {782		Self::do_reschedule_named(id, when)783	}784785	fn next_dispatch_time(id: Vec<u8>) -> Result<T::BlockNumber, ()> {786		Lookup::<T>::get(id)787			.and_then(|(when, index)| Agenda::<T>::get(when).get(index as usize).map(|_| when))788			.ok_or(())789	}790}791792#[cfg(test)]793#[allow(clippy::from_over_into)]794mod tests {795	use super::*;796797	use frame_support::{798		Hashable, assert_err, assert_noop, assert_ok, ord_parameter_types, parameter_types,799		traits::{Contains, OnFinalize, OnInitialize},800		weights::constants::RocksDbWeight,801	};802	use sp_core::H256;803	use sp_runtime::{804		Perbill,805		testing::Header,806		traits::{BlakeTwo256, IdentityLookup},807	};808	use frame_system::{EnsureOneOf, EnsureRoot, EnsureSignedBy};809	use substrate_test_utils::assert_eq_uvec;810	use crate as scheduler;811812	mod logger {813		use super::*;814		use std::cell::RefCell;815816		thread_local! {817			static LOG: RefCell<Vec<(OriginCaller, u32)>> = RefCell::new(Vec::new());818		}819		pub fn log() -> Vec<(OriginCaller, u32)> {820			LOG.with(|log| log.borrow().clone())821		}822		pub trait Config: system::Config {823			type Event: From<Event> + Into<<Self as system::Config>::Event>;824		}825		decl_event! {826			pub enum Event {827				Logged(u32, Weight),828			}829		}830		decl_module! {831			pub struct Module<T: Config> for enum Call832			where833				origin: <T as system::Config>::Origin,834				<T as system::Config>::Origin: OriginTrait<PalletsOrigin = OriginCaller>835			{836				fn deposit_event() = default;837838				#[weight = *weight]839				fn log(origin, i: u32, weight: Weight) {840					Self::deposit_event(Event::Logged(i, weight));841					LOG.with(|log| {842						log.borrow_mut().push((origin.caller().clone(), i));843					})844				}845846				#[weight = *weight]847				fn log_without_filter(origin, i: u32, weight: Weight) {848					Self::deposit_event(Event::Logged(i, weight));849					LOG.with(|log| {850						log.borrow_mut().push((origin.caller().clone(), i));851					})852				}853			}854		}855	}856857	type UncheckedExtrinsic = frame_system::mocking::MockUncheckedExtrinsic<Test>;858	type Block = frame_system::mocking::MockBlock<Test>;859860	frame_support::construct_runtime!(861		pub enum Test where862			Block = Block,863			NodeBlock = Block,864			UncheckedExtrinsic = UncheckedExtrinsic,865		{866			System: frame_system::{Pallet, Call, Config, Storage, Event<T>},867			Logger: logger::{Pallet, Call, Event},868			Scheduler: scheduler::{Pallet, Call, Storage, Event<T>},869		}870	);871872	// Scheduler must dispatch with root and no filter, this tests base filter is indeed not used.873	pub struct BaseFilter;874	impl Contains<Call> for BaseFilter {875		fn contains(call: &Call) -> bool {876			!matches!(call, Call::Logger(logger::Call::log { .. }))877		}878	}879880	parameter_types! {881		pub const BlockHashCount: u64 = 250;882		pub BlockWeights: frame_system::limits::BlockWeights =883			frame_system::limits::BlockWeights::simple_max(2_000_000_000_000);884	}885	impl system::Config for Test {886		type BaseCallFilter = BaseFilter;887		type BlockWeights = ();888		type BlockLength = ();889		type DbWeight = RocksDbWeight;890		type Origin = Origin;891		type Call = Call;892		type Index = u64;893		type BlockNumber = u64;894		type Hash = H256;895		type Hashing = BlakeTwo256;896		type AccountId = u64;897		type Lookup = IdentityLookup<Self::AccountId>;898		type Header = Header;899		type Event = Event;900		type BlockHashCount = BlockHashCount;901		type Version = ();902		type PalletInfo = PalletInfo;903		type AccountData = ();904		type OnNewAccount = ();905		type OnKilledAccount = ();906		type SystemWeightInfo = ();907		type SS58Prefix = ();908		type OnSetCode = ();909	}910	impl logger::Config for Test {911		type Event = Event;912	}913	parameter_types! {914		pub MaximumSchedulerWeight: Weight = Perbill::from_percent(80) * BlockWeights::get().max_block;915		pub const MaxScheduledPerBlock: u32 = 10;916	}917	ord_parameter_types! {918		pub const One: u64 = 1;919	}920921	impl Config for Test {922		type Event = Event;923		type Origin = Origin;924		type PalletsOrigin = OriginCaller;925		type Call = Call;926		type MaximumWeight = MaximumSchedulerWeight;927		type ScheduleOrigin = EnsureOneOf<u64, EnsureRoot<u64>, EnsureSignedBy<One, u64>>;928		type MaxScheduledPerBlock = MaxScheduledPerBlock;929		type WeightInfo = ();930		type SponsorshipHandler = ();931	}932933	pub fn new_test_ext() -> sp_io::TestExternalities {934		let t = system::GenesisConfig::default()935			.build_storage::<Test>()936			.unwrap();937		t.into()938	}939940	fn run_to_block(n: u64) {941		while System::block_number() < n {942			Scheduler::on_finalize(System::block_number());943			System::set_block_number(System::block_number() + 1);944			Scheduler::on_initialize(System::block_number());945		}946	}947948	fn root() -> OriginCaller {949		system::RawOrigin::Root.into()950	}951}