12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152#![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;7879808182838485pub trait Config: system::Config {86 87 type Event: From<Event<Self>> + Into<<Self as system::Config>::Event>;8889 90 type Origin: OriginTrait<PalletsOrigin = Self::PalletsOrigin>91 + From<Self::PalletsOrigin>92 + IsType<<Self as system::Config>::Origin>;9394 95 type PalletsOrigin: From<system::RawOrigin<Self::AccountId>> + Codec + TypeInfo + Clone + Eq;9697 98 type Call: Parameter99 + Dispatchable<Origin = <Self as Config>::Origin>100 + GetDispatchInfo101 + From<system::Call<Self>>;102103 104 105 type MaximumWeight: Get<Weight>;106107 108 type ScheduleOrigin: EnsureOrigin<<Self as system::Config>::Origin>;109110 111 112 type MaxScheduledPerBlock: Get<u32>;113114 115 type SponsorshipHandler: SponsorshipHandler<Self::AccountId, <Self as Config>::Call>;116117 118 type WeightInfo: WeightInfo;119}120121122123124pub type PeriodicIndex = u32;125126pub 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}136137138#[cfg_attr(any(feature = "std", test), derive(PartialEq, Eq))]139#[derive(Clone, RuntimeDebug, Encode, Decode, TypeInfo)]140pub struct ScheduledV2<Call, BlockNumber, PalletsOrigin, AccountId> {141 142 maybe_id: Option<Vec<u8>>,143 144 priority: schedule::Priority,145 146 call: Call,147 148 maybe_periodic: Option<schedule::Period<BlockNumber>>,149 150 origin: PalletsOrigin,151 _phantom: PhantomData<AccountId>,152}153154155pub type Scheduled<Call, BlockNumber, PalletsOrigin, AccountId> =156 ScheduledV2<Call, BlockNumber, PalletsOrigin, AccountId>;157158159160161#[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 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 189 Lookup: map hasher(twox_64_concat) Vec<u8> => Option<TaskAddress<T::BlockNumber>>;190191 192 193 194 StorageVersion build(|_| Releases::V2): Releases;195 }196}197198decl_event!(199 pub enum Event<T> where <T as system::Config>::BlockNumber {200 201 Scheduled(BlockNumber, u32),202 203 Canceled(BlockNumber, u32),204 205 Dispatched(TaskAddress<BlockNumber>, Option<Vec<u8>>, DispatchResult),206 }207);208209decl_error! {210 pub enum Error for Module<T: Config> {211 212 FailedToSchedule,213 214 NotFound,215 216 TargetBlockNumberInPast,217 218 RescheduleNoChange,219 }220}221222decl_module! {223 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 233 234 235 236 237 238 239 240 241 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 255 256 257 258 259 260 261 262 263 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 272 273 274 275 276 277 278 279 280 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 297 298 299 300 301 302 303 304 305 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 314 315 316 317 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 333 334 335 336 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 353 354 355 356 357 358 359 360 361 362 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); 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 391 *cumulative_weight = cumulative_weight392 .saturating_add(T::DbWeight::get().reads_writes(1, 1));393 }394395 if s.maybe_id.is_some() {396 397 *cumulative_weight = cumulative_weight.saturating_add(T::DbWeight::get().writes(1));398 }399 if s.maybe_periodic.is_some() {400 401 *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 408 409 410 411 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 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 465 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 486 let maybe_periodic = maybe_periodic487 .filter(|p| p.1 > 1 && !p.0.is_zero())488 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 573 if Lookup::<T>::contains_key(&id) {574 return Err(Error::<T>::FailedToSchedule.into());575 }576577 let when = Self::resolve_time(when)?;578579 580 let maybe_periodic = maybe_periodic581 .filter(|p| p.1 > 1 && !p.0.is_zero())582 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 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}