1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768#![cfg_attr(not(feature = "std"), no_std)]6970#[cfg(feature = "runtime-benchmarks")]71mod benchmarking;72#[cfg(test)]73mod mock;74#[cfg(test)]75mod tests;76pub mod weights;7778use codec::{Codec, Decode, Encode, MaxEncodedLen};79use frame_support::{80 dispatch::{DispatchError, DispatchResult, Dispatchable, GetDispatchInfo, Parameter},81 traits::{82 schedule::{self, DispatchTime},83 EnsureOrigin, Get, IsType, OriginTrait, PrivilegeCmp, StorageVersion, PreimageRecipient,84 ConstU32,85 },86 weights::Weight,87};8889use frame_system::{self as system};90use scale_info::TypeInfo;91use sp_runtime::{92 traits::{BadOrigin, One, Saturating, Zero, Hash},93 BoundedVec, RuntimeDebug,94};95use sp_std::{borrow::Borrow, cmp::Ordering, marker::PhantomData, prelude::*};96pub use weights::WeightInfo;9798pub use pallet::*;99100101pub type PeriodicIndex = u32;102103pub type TaskAddress<BlockNumber> = (BlockNumber, u32);104105pub type EncodedCall = BoundedVec<u8, ConstU32<128>>;106107#[derive(Clone, Eq, PartialEq, Encode, Decode, RuntimeDebug, TypeInfo, MaxEncodedLen)]108#[scale_info(skip_type_params(T))]109pub enum ScheduledCall<T: Config> {110 Inline(EncodedCall),111 PreimageLookup { hash: T::Hash, unbounded_len: u32 },112}113114impl<T: Config> ScheduledCall<T> {115 pub fn new(call: <T as Config>::Call) -> Result<Self, DispatchError> {116 let encoded = call.encode();117 let len = encoded.len();118119 match EncodedCall::try_from(encoded.clone()) {120 Ok(bounded) => Ok(Self::Inline(bounded)),121 Err(_) => {122 let hash = <T as system::Config>::Hashing::hash_of(&encoded);123 <T as Config>::Preimages::note_preimage(124 encoded125 .try_into()126 .map_err(|_| <Error<T>>::TooBigScheduledCall)?,127 );128129 Ok(Self::PreimageLookup {130 hash,131 unbounded_len: len as u32,132 })133 }134 }135 }136137 138 pub fn lookup_len(&self) -> Option<u32> {139 match self {140 Self::Inline(..) => None,141 Self::PreimageLookup { unbounded_len, .. } => Some(*unbounded_len),142 }143 }144145 146 pub fn lookup_needed(&self) -> bool {147 match self {148 Self::Inline(_) => false,149 Self::PreimageLookup { .. } => true,150 }151 }152153 fn decode(mut data: &[u8]) -> Result<<T as Config>::Call, DispatchError> {154 <T as Config>::Call::decode(&mut data)155 .map_err(|_| <Error<T>>::ScheduledCallCorrupted.into())156 }157}158159pub trait SchedulerPreimages<T: Config>: PreimageRecipient<T::Hash> {160 fn drop(call: &ScheduledCall<T>);161162 fn peek(163 call: &ScheduledCall<T>,164 ) -> Result<(<T as pallet::Config>::Call, Option<u32>), DispatchError>;165166 167 168 169 fn realize(170 call: &ScheduledCall<T>,171 ) -> Result<(<T as pallet::Config>::Call, Option<u32>), DispatchError>;172}173174impl<T: Config, PP: PreimageRecipient<T::Hash>> SchedulerPreimages<T> for PP {175 fn drop(call: &ScheduledCall<T>) {176 match call {177 ScheduledCall::Inline(_) => {}178 ScheduledCall::PreimageLookup { hash, .. } => Self::unrequest_preimage(hash),179 }180 }181182 fn peek(183 call: &ScheduledCall<T>,184 ) -> Result<(<T as pallet::Config>::Call, Option<u32>), DispatchError> {185 match call {186 ScheduledCall::Inline(data) => Ok((ScheduledCall::<T>::decode(data)?, None)),187 ScheduledCall::PreimageLookup {188 hash,189 unbounded_len,190 } => {191 let (preimage, len) = Self::get_preimage(hash)192 .ok_or(<Error<T>>::PreimageNotFound)193 .map(|preimage| (preimage, *unbounded_len))?;194195 Ok((ScheduledCall::<T>::decode(preimage.as_slice())?, Some(len)))196 }197 }198 }199200 fn realize(201 call: &ScheduledCall<T>,202 ) -> Result<(<T as pallet::Config>::Call, Option<u32>), DispatchError> {203 let r = Self::peek(call)?;204 Self::drop(call);205 Ok(r)206 }207}208209pub type TaskName = [u8; 32];210211212#[cfg_attr(any(feature = "std", test), derive(PartialEq, Eq))]213#[derive(Clone, RuntimeDebug, Encode, Decode, MaxEncodedLen, TypeInfo)]214pub struct Scheduled<Name, Call, BlockNumber, PalletsOrigin, AccountId> {215 216 maybe_id: Option<Name>,217218 219 priority: schedule::Priority,220221 222 call: Call,223224 225 maybe_periodic: Option<schedule::Period<BlockNumber>>,226227 228 origin: PalletsOrigin,229 _phantom: PhantomData<AccountId>,230}231232pub type ScheduledOf<T> = Scheduled<233 TaskName,234 ScheduledCall<T>,235 <T as frame_system::Config>::BlockNumber,236 <T as Config>::PalletsOrigin,237 <T as frame_system::Config>::AccountId,238>;239240struct WeightCounter {241 used: Weight,242 limit: Weight,243}244245impl WeightCounter {246 fn check_accrue(&mut self, w: Weight) -> bool {247 let test = self.used.saturating_add(w);248 if test > self.limit {249 false250 } else {251 self.used = test;252 true253 }254 }255256 fn can_accrue(&mut self, w: Weight) -> bool {257 self.used.saturating_add(w) <= self.limit258 }259}260261pub(crate) trait MarginalWeightInfo: WeightInfo {262 fn service_task(maybe_lookup_len: Option<usize>, named: bool, periodic: bool) -> Weight {263 let base = Self::service_task_base();264 let mut total = match maybe_lookup_len {265 None => base,266 Some(l) => Self::service_task_fetched(l as u32),267 };268 if named {269 total.saturating_accrue(Self::service_task_named().saturating_sub(base));270 }271 if periodic {272 total.saturating_accrue(Self::service_task_periodic().saturating_sub(base));273 }274 total275 }276}277278impl<T: WeightInfo> MarginalWeightInfo for T {}279280#[frame_support::pallet]281pub mod pallet {282 use super::*;283 use frame_support::{dispatch::PostDispatchInfo, pallet_prelude::*};284 use system::pallet_prelude::*;285286 287 const STORAGE_VERSION: StorageVersion = StorageVersion::new(0);288289 #[pallet::pallet]290 #[pallet::generate_store(pub(super) trait Store)]291 #[pallet::storage_version(STORAGE_VERSION)]292 pub struct Pallet<T>(_);293294 #[pallet::config]295 pub trait Config: frame_system::Config {296 type Event: From<Event<Self>> + IsType<<Self as frame_system::Config>::Event>;297298 299 type Origin: OriginTrait<PalletsOrigin = Self::PalletsOrigin>300 + From<Self::PalletsOrigin>301 + IsType<<Self as system::Config>::Origin>302 + Clone;303304 305 type PalletsOrigin: From<system::RawOrigin<Self::AccountId>>306 + Codec307 + Clone308 + Eq309 + TypeInfo310 + MaxEncodedLen;311312 313 type Call: Parameter314 + Dispatchable<Origin = <Self as Config>::Origin, PostInfo = PostDispatchInfo>315 + GetDispatchInfo316 + From<system::Call<Self>>;317318 319 #[pallet::constant]320 type MaximumWeight: Get<Weight>;321322 323 type ScheduleOrigin: EnsureOrigin<<Self as system::Config>::Origin>;324325 326 327 328 329 330 331 332 type OriginPrivilegeCmp: PrivilegeCmp<Self::PalletsOrigin>;333334 335 #[pallet::constant]336 type MaxScheduledPerBlock: Get<u32>;337338 339 type WeightInfo: WeightInfo;340341 342 type Preimages: SchedulerPreimages<Self>;343 }344345 #[pallet::storage]346 pub type IncompleteSince<T: Config> = StorageValue<_, T::BlockNumber>;347348 349 #[pallet::storage]350 pub type Agenda<T: Config> = StorageMap<351 _,352 Twox64Concat,353 T::BlockNumber,354 BoundedVec<Option<ScheduledOf<T>>, T::MaxScheduledPerBlock>,355 ValueQuery,356 >;357358 359 #[pallet::storage]360 pub(crate) type Lookup<T: Config> =361 StorageMap<_, Twox64Concat, TaskName, TaskAddress<T::BlockNumber>>;362363 364 #[pallet::event]365 #[pallet::generate_deposit(pub(super) fn deposit_event)]366 pub enum Event<T: Config> {367 368 Scheduled { when: T::BlockNumber, index: u32 },369 370 Canceled { when: T::BlockNumber, index: u32 },371 372 Dispatched {373 task: TaskAddress<T::BlockNumber>,374 id: Option<[u8; 32]>,375 result: DispatchResult,376 },377 378 CallUnavailable {379 task: TaskAddress<T::BlockNumber>,380 id: Option<[u8; 32]>,381 },382 383 PeriodicFailed {384 task: TaskAddress<T::BlockNumber>,385 id: Option<[u8; 32]>,386 },387 388 PermanentlyOverweight {389 task: TaskAddress<T::BlockNumber>,390 id: Option<[u8; 32]>,391 },392 }393394 #[pallet::error]395 pub enum Error<T> {396 397 FailedToSchedule,398 399 AgendaIsExhausted,400 401 ScheduledCallCorrupted,402 403 PreimageNotFound,404 405 TooBigScheduledCall,406 407 NotFound,408 409 TargetBlockNumberInPast,410 411 RescheduleNoChange,412 413 Named,414 }415416 #[pallet::hooks]417 impl<T: Config> Hooks<BlockNumberFor<T>> for Pallet<T> {418 419 fn on_initialize(now: T::BlockNumber) -> Weight {420 let mut weight_counter = WeightCounter {421 used: Weight::zero(),422 limit: T::MaximumWeight::get(),423 };424 Self::service_agendas(&mut weight_counter, now, u32::max_value());425 weight_counter.used426 }427 }428429 #[pallet::call]430 impl<T: Config> Pallet<T> {431 432 #[pallet::weight(<T as Config>::WeightInfo::schedule(T::MaxScheduledPerBlock::get()))]433 pub fn schedule(434 origin: OriginFor<T>,435 when: T::BlockNumber,436 maybe_periodic: Option<schedule::Period<T::BlockNumber>>,437 priority: schedule::Priority,438 call: Box<<T as Config>::Call>,439 ) -> DispatchResult {440 T::ScheduleOrigin::ensure_origin(origin.clone())?;441 let origin = <T as Config>::Origin::from(origin);442 Self::do_schedule(443 DispatchTime::At(when),444 maybe_periodic,445 priority,446 origin.caller().clone(),447 <ScheduledCall<T>>::new(*call)?,448 )?;449 Ok(())450 }451452 453 #[pallet::weight(<T as Config>::WeightInfo::cancel(T::MaxScheduledPerBlock::get()))]454 pub fn cancel(origin: OriginFor<T>, when: T::BlockNumber, index: u32) -> DispatchResult {455 T::ScheduleOrigin::ensure_origin(origin.clone())?;456 let origin = <T as Config>::Origin::from(origin);457 Self::do_cancel(Some(origin.caller().clone()), (when, index))?;458 Ok(())459 }460461 462 #[pallet::weight(<T as Config>::WeightInfo::schedule_named(T::MaxScheduledPerBlock::get()))]463 pub fn schedule_named(464 origin: OriginFor<T>,465 id: TaskName,466 when: T::BlockNumber,467 maybe_periodic: Option<schedule::Period<T::BlockNumber>>,468 priority: schedule::Priority,469 call: Box<<T as Config>::Call>,470 ) -> DispatchResult {471 T::ScheduleOrigin::ensure_origin(origin.clone())?;472 let origin = <T as Config>::Origin::from(origin);473 Self::do_schedule_named(474 id,475 DispatchTime::At(when),476 maybe_periodic,477 priority,478 origin.caller().clone(),479 <ScheduledCall<T>>::new(*call)?,480 )?;481 Ok(())482 }483484 485 #[pallet::weight(<T as Config>::WeightInfo::cancel_named(T::MaxScheduledPerBlock::get()))]486 pub fn cancel_named(origin: OriginFor<T>, id: TaskName) -> DispatchResult {487 T::ScheduleOrigin::ensure_origin(origin.clone())?;488 let origin = <T as Config>::Origin::from(origin);489 Self::do_cancel_named(Some(origin.caller().clone()), id)?;490 Ok(())491 }492493 494 495 496 497 498 #[pallet::weight(<T as Config>::WeightInfo::schedule(T::MaxScheduledPerBlock::get()))]499 pub fn schedule_after(500 origin: OriginFor<T>,501 after: T::BlockNumber,502 maybe_periodic: Option<schedule::Period<T::BlockNumber>>,503 priority: schedule::Priority,504 call: Box<<T as Config>::Call>,505 ) -> DispatchResult {506 T::ScheduleOrigin::ensure_origin(origin.clone())?;507 let origin = <T as Config>::Origin::from(origin);508 Self::do_schedule(509 DispatchTime::After(after),510 maybe_periodic,511 priority,512 origin.caller().clone(),513 <ScheduledCall<T>>::new(*call)?,514 )?;515 Ok(())516 }517518 519 520 521 522 523 #[pallet::weight(<T as Config>::WeightInfo::schedule_named(T::MaxScheduledPerBlock::get()))]524 pub fn schedule_named_after(525 origin: OriginFor<T>,526 id: TaskName,527 after: T::BlockNumber,528 maybe_periodic: Option<schedule::Period<T::BlockNumber>>,529 priority: schedule::Priority,530 call: Box<<T as Config>::Call>,531 ) -> DispatchResult {532 T::ScheduleOrigin::ensure_origin(origin.clone())?;533 let origin = <T as Config>::Origin::from(origin);534 Self::do_schedule_named(535 id,536 DispatchTime::After(after),537 maybe_periodic,538 priority,539 origin.caller().clone(),540 <ScheduledCall<T>>::new(*call)?,541 )?;542 Ok(())543 }544 }545}546547impl<T: Config> Pallet<T> {548 fn resolve_time(when: DispatchTime<T::BlockNumber>) -> Result<T::BlockNumber, DispatchError> {549 let now = frame_system::Pallet::<T>::block_number();550551 let when = match when {552 DispatchTime::At(x) => x,553 554 555 DispatchTime::After(x) => now.saturating_add(x).saturating_add(One::one()),556 };557558 if when <= now {559 return Err(Error::<T>::TargetBlockNumberInPast.into());560 }561562 Ok(when)563 }564565 fn place_task(566 when: T::BlockNumber,567 what: ScheduledOf<T>,568 ) -> Result<TaskAddress<T::BlockNumber>, (DispatchError, ScheduledOf<T>)> {569 let maybe_name = what.maybe_id;570 let index = Self::push_to_agenda(when, what)?;571 let address = (when, index);572 if let Some(name) = maybe_name {573 Lookup::<T>::insert(name, address)574 }575 Self::deposit_event(Event::Scheduled {576 when: address.0,577 index: address.1,578 });579 Ok(address)580 }581582 fn push_to_agenda(583 when: T::BlockNumber,584 what: ScheduledOf<T>,585 ) -> Result<u32, (DispatchError, ScheduledOf<T>)> {586 let mut agenda = Agenda::<T>::get(when);587 let index = if (agenda.len() as u32) < T::MaxScheduledPerBlock::get() {588 589 let _ = agenda.try_push(Some(what));590 agenda.len() as u32 - 1591 } else {592 if let Some(hole_index) = agenda.iter().position(|i| i.is_none()) {593 agenda[hole_index] = Some(what);594 hole_index as u32595 } else {596 return Err((<Error<T>>::AgendaIsExhausted.into(), what));597 }598 };599 Agenda::<T>::insert(when, agenda);600 Ok(index)601 }602603 fn do_schedule(604 when: DispatchTime<T::BlockNumber>,605 maybe_periodic: Option<schedule::Period<T::BlockNumber>>,606 priority: schedule::Priority,607 origin: T::PalletsOrigin,608 call: ScheduledCall<T>,609 ) -> Result<TaskAddress<T::BlockNumber>, DispatchError> {610 let when = Self::resolve_time(when)?;611612 613 let maybe_periodic = maybe_periodic614 .filter(|p| p.1 > 1 && !p.0.is_zero())615 616 .map(|(p, c)| (p, c - 1));617 let task = Scheduled {618 maybe_id: None,619 priority,620 call,621 maybe_periodic,622 origin,623 _phantom: PhantomData,624 };625 Self::place_task(when, task).map_err(|x| x.0)626 }627628 fn do_cancel(629 origin: Option<T::PalletsOrigin>,630 (when, index): TaskAddress<T::BlockNumber>,631 ) -> Result<(), DispatchError> {632 let scheduled = Agenda::<T>::try_mutate(when, |agenda| {633 agenda.get_mut(index as usize).map_or(634 Ok(None),635 |s| -> Result<Option<Scheduled<_, _, _, _, _>>, DispatchError> {636 if let (Some(ref o), Some(ref s)) = (origin, s.borrow()) {637 if matches!(638 T::OriginPrivilegeCmp::cmp_privilege(o, &s.origin),639 Some(Ordering::Less) | None640 ) {641 return Err(BadOrigin.into());642 }643 };644 Ok(s.take())645 },646 )647 })?;648 if let Some(s) = scheduled {649 T::Preimages::drop(&s.call);650651 if let Some(id) = s.maybe_id {652 Lookup::<T>::remove(id);653 }654 Self::deposit_event(Event::Canceled { when, index });655 Ok(())656 } else {657 return Err(Error::<T>::NotFound.into());658 }659 }660661 fn do_schedule_named(662 id: TaskName,663 when: DispatchTime<T::BlockNumber>,664 maybe_periodic: Option<schedule::Period<T::BlockNumber>>,665 priority: schedule::Priority,666 origin: T::PalletsOrigin,667 call: ScheduledCall<T>,668 ) -> Result<TaskAddress<T::BlockNumber>, DispatchError> {669 670 if Lookup::<T>::contains_key(&id) {671 return Err(Error::<T>::FailedToSchedule.into());672 }673674 let when = Self::resolve_time(when)?;675676 677 let maybe_periodic = maybe_periodic678 .filter(|p| p.1 > 1 && !p.0.is_zero())679 680 .map(|(p, c)| (p, c - 1));681682 let task = Scheduled {683 maybe_id: Some(id),684 priority,685 call,686 maybe_periodic,687 origin,688 _phantom: Default::default(),689 };690 Self::place_task(when, task).map_err(|x| x.0)691 }692693 fn do_cancel_named(origin: Option<T::PalletsOrigin>, id: TaskName) -> DispatchResult {694 Lookup::<T>::try_mutate_exists(id, |lookup| -> DispatchResult {695 if let Some((when, index)) = lookup.take() {696 let i = index as usize;697 Agenda::<T>::try_mutate(when, |agenda| -> DispatchResult {698 if let Some(s) = agenda.get_mut(i) {699 if let (Some(ref o), Some(ref s)) = (origin, s.borrow()) {700 if matches!(701 T::OriginPrivilegeCmp::cmp_privilege(o, &s.origin),702 Some(Ordering::Less) | None703 ) {704 return Err(BadOrigin.into());705 }706 T::Preimages::drop(&s.call);707 }708 *s = None;709 }710 Ok(())711 })?;712 Self::deposit_event(Event::Canceled { when, index });713 Ok(())714 } else {715 return Err(Error::<T>::NotFound.into());716 }717 })718 }719}720721enum ServiceTaskError {722 723 Unavailable,724 725 Overweight,726}727use ServiceTaskError::*;728729impl<T: Config> Pallet<T> {730 731 fn service_agendas(weight: &mut WeightCounter, now: T::BlockNumber, max: u32) {732 if !weight.check_accrue(T::WeightInfo::service_agendas_base()) {733 return;734 }735736 let mut incomplete_since = now + One::one();737 let mut when = IncompleteSince::<T>::take().unwrap_or(now);738 let mut executed = 0;739740 let max_items = T::MaxScheduledPerBlock::get();741 let mut count_down = max;742 let service_agenda_base_weight = T::WeightInfo::service_agenda_base(max_items);743 while count_down > 0 && when <= now && weight.can_accrue(service_agenda_base_weight) {744 if !Self::service_agenda(weight, &mut executed, now, when, u32::max_value()) {745 incomplete_since = incomplete_since.min(when);746 }747 when.saturating_inc();748 count_down.saturating_dec();749 }750 incomplete_since = incomplete_since.min(when);751 if incomplete_since <= now {752 IncompleteSince::<T>::put(incomplete_since);753 }754 }755756 757 758 fn service_agenda(759 weight: &mut WeightCounter,760 executed: &mut u32,761 now: T::BlockNumber,762 when: T::BlockNumber,763 max: u32,764 ) -> bool {765 let mut agenda = Agenda::<T>::get(when);766 let mut ordered = agenda767 .iter()768 .enumerate()769 .filter_map(|(index, maybe_item)| {770 maybe_item771 .as_ref()772 .map(|item| (index as u32, item.priority))773 })774 .collect::<Vec<_>>();775 ordered.sort_by_key(|k| k.1);776 let within_limit =777 weight.check_accrue(T::WeightInfo::service_agenda_base(ordered.len() as u32));778 debug_assert!(779 within_limit,780 "weight limit should have been checked in advance"781 );782783 784 let mut postponed = (ordered.len() as u32).saturating_sub(max);785 786 let mut dropped = 0;787788 for (agenda_index, _) in ordered.into_iter().take(max as usize) {789 let task = match agenda[agenda_index as usize].take() {790 None => continue,791 Some(t) => t,792 };793 let base_weight = T::WeightInfo::service_task(794 task.call.lookup_len().map(|x| x as usize),795 task.maybe_id.is_some(),796 task.maybe_periodic.is_some(),797 );798 if !weight.can_accrue(base_weight) {799 postponed += 1;800 break;801 }802 let result = Self::service_task(weight, now, when, agenda_index, *executed == 0, task);803 agenda[agenda_index as usize] = match result {804 Err((Unavailable, slot)) => {805 dropped += 1;806 slot807 }808 Err((Overweight, slot)) => {809 postponed += 1;810 slot811 }812 Ok(()) => {813 *executed += 1;814 None815 }816 };817 }818 if postponed > 0 || dropped > 0 {819 Agenda::<T>::insert(when, agenda);820 } else {821 Agenda::<T>::remove(when);822 }823 postponed == 0824 }825826 827 828 829 830 831 832 fn service_task(833 weight: &mut WeightCounter,834 now: T::BlockNumber,835 when: T::BlockNumber,836 agenda_index: u32,837 is_first: bool,838 mut task: ScheduledOf<T>,839 ) -> Result<(), (ServiceTaskError, Option<ScheduledOf<T>>)> {840 if let Some(ref id) = task.maybe_id {841 Lookup::<T>::remove(id);842 }843844 let (call, lookup_len) = match T::Preimages::peek(&task.call) {845 Ok(c) => c,846 Err(_) => return Err((Unavailable, Some(task))),847 };848849 weight.check_accrue(T::WeightInfo::service_task(850 lookup_len.map(|x| x as usize),851 task.maybe_id.is_some(),852 task.maybe_periodic.is_some(),853 ));854855 match Self::execute_dispatch(weight, task.origin.clone(), call) {856 Err(Unavailable) => {857 debug_assert!(false, "Checked to exist with `peek`");858 Self::deposit_event(Event::CallUnavailable {859 task: (when, agenda_index),860 id: task.maybe_id,861 });862 Err((Unavailable, Some(task)))863 }864 Err(Overweight) if is_first => {865 T::Preimages::drop(&task.call);866 Self::deposit_event(Event::PermanentlyOverweight {867 task: (when, agenda_index),868 id: task.maybe_id,869 });870 Err((Unavailable, Some(task)))871 }872 Err(Overweight) => Err((Overweight, Some(task))),873 Ok(result) => {874 Self::deposit_event(Event::Dispatched {875 task: (when, agenda_index),876 id: task.maybe_id,877 result,878 });879 if let &Some((period, count)) = &task.maybe_periodic {880 if count > 1 {881 task.maybe_periodic = Some((period, count - 1));882 } else {883 task.maybe_periodic = None;884 }885 let wake = now.saturating_add(period);886 match Self::place_task(wake, task) {887 Ok(_) => {}888 Err((_, task)) => {889 890 891 T::Preimages::drop(&task.call);892 Self::deposit_event(Event::PeriodicFailed {893 task: (when, agenda_index),894 id: task.maybe_id,895 });896 }897 }898 } else {899 T::Preimages::drop(&task.call);900 }901 Ok(())902 }903 }904 }905906 907 908 909 910 911 912 fn execute_dispatch(913 weight: &mut WeightCounter,914 origin: T::PalletsOrigin,915 call: <T as Config>::Call,916 ) -> Result<DispatchResult, ServiceTaskError> {917 let dispatch_origin: <T as Config>::Origin = origin.into();918 let base_weight = match dispatch_origin.clone().as_signed() {919 Some(_) => T::WeightInfo::execute_dispatch_signed(),920 _ => T::WeightInfo::execute_dispatch_unsigned(),921 };922 let call_weight = call.get_dispatch_info().weight;923 924 let max_weight = base_weight.saturating_add(call_weight);925926 if !weight.can_accrue(max_weight) {927 return Err(Overweight);928 }929930 let (maybe_actual_call_weight, result) = match call.dispatch(dispatch_origin) {931 Ok(post_info) => (post_info.actual_weight, Ok(())),932 Err(error_and_info) => (933 error_and_info.post_info.actual_weight,934 Err(error_and_info.error),935 ),936 };937 let call_weight = maybe_actual_call_weight.unwrap_or(call_weight);938 weight.check_accrue(base_weight);939 weight.check_accrue(call_weight);940 Ok(result)941 }942}