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, 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}