difftreelog
Merge branch 'feature/internal-audit' of github.com:UniqueNetwork/unique-chain into feature/internal-audit
in: master
2 files changed
pallets/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
///
pallets/scheduler/src/lib.rsdiffbeforeafterboth1// 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}