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, 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;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 460 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 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 522 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 543 let maybe_periodic = maybe_periodic544 .filter(|p| p.1 > 1 && !p.0.is_zero())545 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 630 if Lookup::<T>::contains_key(&id) {631 return Err(Error::<T>::FailedToSchedule.into());632 }633634 let when = Self::resolve_time(when)?;635636 637 let maybe_periodic = maybe_periodic638 .filter(|p| p.1 > 1 && !p.0.is_zero())639 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 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}