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::{81 DispatchError, DispatchResult, Dispatchable, GetDispatchInfo, Parameter, RawOrigin,82 },83 ensure,84 traits::{85 schedule::{self, DispatchTime},86 EnsureOrigin, Get, IsType, OriginTrait,87 PalletInfoAccess, PrivilegeCmp, StorageVersion,88 PreimageProvider, PreimageRecipient, ConstU32,89 },90 weights::Weight,91};9293use frame_system::{self as system};94use scale_info::TypeInfo;95use sp_io::hashing::blake2_256;96use sp_runtime::{97 traits::{BadOrigin, One, Saturating, Zero, Hash},98 BoundedVec, RuntimeDebug,99};100use sp_std::{borrow::Borrow, cmp::Ordering, marker::PhantomData, prelude::*};101pub use weights::WeightInfo;102103pub use pallet::*;104105106pub type PeriodicIndex = u32;107108pub type TaskAddress<BlockNumber> = (BlockNumber, u32);109110pub type EncodedCall = BoundedVec<u8, ConstU32<128>>;111112#[derive(Clone, Eq, PartialEq, Encode, Decode, RuntimeDebug, TypeInfo, MaxEncodedLen)]113#[scale_info(skip_type_params(T))]114pub enum ScheduledCall<T: Config> {115 Inline(EncodedCall),116 PreimageLookup {117 hash: T::Hash,118 unbounded_len: u32,119 },120}121122impl<T: Config> ScheduledCall<T> {123 pub fn new(call: <T as Config>::Call) -> Result<Self, DispatchError> {124 let encoded = call.encode();125 let len = encoded.len();126127 match EncodedCall::try_from(encoded.clone()) {128 Ok(bounded) => Ok(Self::Inline(bounded)),129 Err(_) => {130 let hash = <T as system::Config>::Hashing::hash_of(&encoded);131 <T as Config>::Preimages::note_preimage(encoded.try_into().map_err(|_| <Error<T>>::TooBigScheduledCall)?);132133 Ok(Self::PreimageLookup { hash, unbounded_len: len as u32 })134 }135 }136 }137138 139 pub fn lookup_len(&self) -> Option<u32> {140 match self {141 Self::Inline(..) => None,142 Self::PreimageLookup { unbounded_len, .. } => Some(*unbounded_len),143 }144 }145146 147 pub fn lookup_needed(&self) -> bool {148 match self {149 Self::Inline(_) => false,150 Self::PreimageLookup { .. } => true,151 }152 }153154 fn decode(mut data: &[u8]) -> Result<<T as Config>::Call, DispatchError> {155 <T as Config>::Call::decode(&mut data)156 .map_err(|_| <Error<T>>::ScheduledCallCorrupted.into())157 }158}159160pub trait SchedulerPreimages<T: Config>: PreimageRecipient<T::Hash> {161 fn drop(call: &ScheduledCall<T>);162163 fn peek(call: &ScheduledCall<T>) -> Result<(<T as pallet::Config>::Call, Option<u32>), DispatchError>;164165 166 167 168 fn realize(call: &ScheduledCall<T>) -> Result<(<T as pallet::Config>::Call, Option<u32>), DispatchError>;169}170171impl<T: Config, PP: PreimageRecipient<T::Hash>> SchedulerPreimages<T> for PP {172 fn drop(call: &ScheduledCall<T>) {173 match call {174 ScheduledCall::Inline(_) => {},175 ScheduledCall::PreimageLookup { hash, .. } => Self::unrequest_preimage(hash),176 }177 }178179 fn peek(call: &ScheduledCall<T>) -> Result<(<T as pallet::Config>::Call, Option<u32>), DispatchError> {180 match call {181 ScheduledCall::Inline(data) => Ok((ScheduledCall::<T>::decode(data)?, None)),182 ScheduledCall::PreimageLookup { hash, unbounded_len } => {183 let (preimage, len) = Self::get_preimage(hash)184 .ok_or(<Error<T>>::PreimageNotFound)185 .map(|preimage| (preimage, *unbounded_len))?;186187 Ok((ScheduledCall::<T>::decode(preimage.as_slice())?, Some(len)))188 },189 }190 }191192 fn realize(call: &ScheduledCall<T>) -> Result<(<T as pallet::Config>::Call, Option<u32>), DispatchError> {193 let r = Self::peek(call)?;194 Self::drop(call);195 Ok(r)196 }197}198199pub type TaskName = [u8; 32];200201202#[cfg_attr(any(feature = "std", test), derive(PartialEq, Eq))]203#[derive(Clone, RuntimeDebug, Encode, Decode, MaxEncodedLen, TypeInfo)]204pub struct Scheduled<Name, Call, BlockNumber, PalletsOrigin, AccountId> {205 206 maybe_id: Option<Name>,207208 209 priority: schedule::Priority,210211 212 call: Call,213214 215 maybe_periodic: Option<schedule::Period<BlockNumber>>,216217 218 origin: PalletsOrigin,219 _phantom: PhantomData<AccountId>,220}221222pub type ScheduledOf<T> = Scheduled<223 TaskName,224 ScheduledCall<T>,225 <T as frame_system::Config>::BlockNumber,226 <T as Config>::PalletsOrigin,227 <T as frame_system::Config>::AccountId,228>;229230struct WeightCounter {231 used: Weight,232 limit: Weight,233}234235impl WeightCounter {236 fn check_accrue(&mut self, w: Weight) -> bool {237 let test = self.used.saturating_add(w);238 if test > self.limit {239 false240 } else {241 self.used = test;242 true243 }244 }245246 fn can_accrue(&mut self, w: Weight) -> bool {247 self.used.saturating_add(w) <= self.limit248 }249}250251pub(crate) trait MarginalWeightInfo: WeightInfo {252 fn service_task(maybe_lookup_len: Option<usize>, named: bool, periodic: bool) -> Weight {253 let base = Self::service_task_base();254 let mut total = match maybe_lookup_len {255 None => base,256 Some(l) => Self::service_task_fetched(l as u32),257 };258 if named {259 total.saturating_accrue(Self::service_task_named().saturating_sub(base));260 }261 if periodic {262 total.saturating_accrue(Self::service_task_periodic().saturating_sub(base));263 }264 total265 }266}267268impl<T: WeightInfo> MarginalWeightInfo for T {}269270#[frame_support::pallet]271pub mod pallet {272 use super::*;273 use frame_support::{dispatch::PostDispatchInfo, pallet_prelude::*};274 use system::pallet_prelude::*;275276 277 const STORAGE_VERSION: StorageVersion = StorageVersion::new(0);278279 #[pallet::pallet]280 #[pallet::generate_store(pub(super) trait Store)]281 #[pallet::storage_version(STORAGE_VERSION)]282 pub struct Pallet<T>(_);283284 #[pallet::config]285 pub trait Config: frame_system::Config {286 type Event: From<Event<Self>> + IsType<<Self as frame_system::Config>::Event>;287288 289 type Origin: OriginTrait<PalletsOrigin = Self::PalletsOrigin>290 + From<Self::PalletsOrigin>291 + IsType<<Self as system::Config>::Origin>292 + Clone;293294 295 type PalletsOrigin: From<system::RawOrigin<Self::AccountId>>296 + Codec297 + Clone298 + Eq299 + TypeInfo300 + MaxEncodedLen;301302 303 type Call: Parameter304 + Dispatchable<305 Origin = <Self as Config>::Origin,306 PostInfo = PostDispatchInfo,307 > + GetDispatchInfo308 + From<system::Call<Self>>;309310 311 #[pallet::constant]312 type MaximumWeight: Get<Weight>;313314 315 type ScheduleOrigin: EnsureOrigin<<Self as system::Config>::Origin>;316317 318 319 320 321 322 323 324 type OriginPrivilegeCmp: PrivilegeCmp<Self::PalletsOrigin>;325326 327 #[pallet::constant]328 type MaxScheduledPerBlock: Get<u32>;329330 331 type WeightInfo: WeightInfo;332333 334 type Preimages: SchedulerPreimages<Self>;335 }336337 #[pallet::storage]338 pub type IncompleteSince<T: Config> = StorageValue<_, T::BlockNumber>;339340 341 #[pallet::storage]342 pub type Agenda<T: Config> = StorageMap<343 _,344 Twox64Concat,345 T::BlockNumber,346 BoundedVec<Option<ScheduledOf<T>>, T::MaxScheduledPerBlock>,347 ValueQuery,348 >;349350 351 #[pallet::storage]352 pub(crate) type Lookup<T: Config> =353 StorageMap<_, Twox64Concat, TaskName, TaskAddress<T::BlockNumber>>;354355 356 #[pallet::event]357 #[pallet::generate_deposit(pub(super) fn deposit_event)]358 pub enum Event<T: Config> {359 360 Scheduled { when: T::BlockNumber, index: u32 },361 362 Canceled { when: T::BlockNumber, index: u32 },363 364 Dispatched {365 task: TaskAddress<T::BlockNumber>,366 id: Option<[u8; 32]>,367 result: DispatchResult,368 },369 370 CallUnavailable { task: TaskAddress<T::BlockNumber>, id: Option<[u8; 32]> },371 372 PeriodicFailed { task: TaskAddress<T::BlockNumber>, id: Option<[u8; 32]> },373 374 PermanentlyOverweight { task: TaskAddress<T::BlockNumber>, id: Option<[u8; 32]> },375 }376377 #[pallet::error]378 pub enum Error<T> {379 380 FailedToSchedule,381 382 AgendaIsExhausted,383 384 ScheduledCallCorrupted,385 386 PreimageNotFound,387 388 TooBigScheduledCall,389 390 NotFound,391 392 TargetBlockNumberInPast,393 394 RescheduleNoChange,395 396 Named,397 }398399 #[pallet::hooks]400 impl<T: Config> Hooks<BlockNumberFor<T>> for Pallet<T> {401 402 fn on_initialize(now: T::BlockNumber) -> Weight {403 let mut weight_counter =404 WeightCounter { used: Weight::zero(), limit: T::MaximumWeight::get() };405 Self::service_agendas(&mut weight_counter, now, u32::max_value());406 weight_counter.used407 }408 }409410 #[pallet::call]411 impl<T: Config> Pallet<T> {412 413 #[pallet::weight(<T as Config>::WeightInfo::schedule(T::MaxScheduledPerBlock::get()))]414 pub fn schedule(415 origin: OriginFor<T>,416 when: T::BlockNumber,417 maybe_periodic: Option<schedule::Period<T::BlockNumber>>,418 priority: schedule::Priority,419 call: Box<<T as Config>::Call>,420 ) -> DispatchResult {421 T::ScheduleOrigin::ensure_origin(origin.clone())?;422 let origin = <T as Config>::Origin::from(origin);423 Self::do_schedule(424 DispatchTime::At(when),425 maybe_periodic,426 priority,427 origin.caller().clone(),428 <ScheduledCall<T>>::new(*call)?,429 )?;430 Ok(())431 }432433 434 #[pallet::weight(<T as Config>::WeightInfo::cancel(T::MaxScheduledPerBlock::get()))]435 pub fn cancel(origin: OriginFor<T>, when: T::BlockNumber, index: u32) -> DispatchResult {436 T::ScheduleOrigin::ensure_origin(origin.clone())?;437 let origin = <T as Config>::Origin::from(origin);438 Self::do_cancel(Some(origin.caller().clone()), (when, index))?;439 Ok(())440 }441442 443 #[pallet::weight(<T as Config>::WeightInfo::schedule_named(T::MaxScheduledPerBlock::get()))]444 pub fn schedule_named(445 origin: OriginFor<T>,446 id: TaskName,447 when: T::BlockNumber,448 maybe_periodic: Option<schedule::Period<T::BlockNumber>>,449 priority: schedule::Priority,450 call: Box<<T as Config>::Call>,451 ) -> DispatchResult {452 T::ScheduleOrigin::ensure_origin(origin.clone())?;453 let origin = <T as Config>::Origin::from(origin);454 Self::do_schedule_named(455 id,456 DispatchTime::At(when),457 maybe_periodic,458 priority,459 origin.caller().clone(),460 <ScheduledCall<T>>::new(*call)?,461 )?;462 Ok(())463 }464465 466 #[pallet::weight(<T as Config>::WeightInfo::cancel_named(T::MaxScheduledPerBlock::get()))]467 pub fn cancel_named(origin: OriginFor<T>, id: TaskName) -> DispatchResult {468 T::ScheduleOrigin::ensure_origin(origin.clone())?;469 let origin = <T as Config>::Origin::from(origin);470 Self::do_cancel_named(Some(origin.caller().clone()), id)?;471 Ok(())472 }473474 475 476 477 478 479 #[pallet::weight(<T as Config>::WeightInfo::schedule(T::MaxScheduledPerBlock::get()))]480 pub fn schedule_after(481 origin: OriginFor<T>,482 after: T::BlockNumber,483 maybe_periodic: Option<schedule::Period<T::BlockNumber>>,484 priority: schedule::Priority,485 call: Box<<T as Config>::Call>,486 ) -> DispatchResult {487 T::ScheduleOrigin::ensure_origin(origin.clone())?;488 let origin = <T as Config>::Origin::from(origin);489 Self::do_schedule(490 DispatchTime::After(after),491 maybe_periodic,492 priority,493 origin.caller().clone(),494 <ScheduledCall<T>>::new(*call)?,495 )?;496 Ok(())497 }498499 500 501 502 503 504 #[pallet::weight(<T as Config>::WeightInfo::schedule_named(T::MaxScheduledPerBlock::get()))]505 pub fn schedule_named_after(506 origin: OriginFor<T>,507 id: TaskName,508 after: T::BlockNumber,509 maybe_periodic: Option<schedule::Period<T::BlockNumber>>,510 priority: schedule::Priority,511 call: Box<<T as Config>::Call>,512 ) -> DispatchResult {513 T::ScheduleOrigin::ensure_origin(origin.clone())?;514 let origin = <T as Config>::Origin::from(origin);515 Self::do_schedule_named(516 id,517 DispatchTime::After(after),518 maybe_periodic,519 priority,520 origin.caller().clone(),521 <ScheduledCall<T>>::new(*call)?,522 )?;523 Ok(())524 }525 }526}527528impl<T: Config> Pallet<T> {529 fn resolve_time(when: DispatchTime<T::BlockNumber>) -> Result<T::BlockNumber, DispatchError> {530 let now = frame_system::Pallet::<T>::block_number();531532 let when = match when {533 DispatchTime::At(x) => x,534 535 536 DispatchTime::After(x) => now.saturating_add(x).saturating_add(One::one()),537 };538539 if when <= now {540 return Err(Error::<T>::TargetBlockNumberInPast.into())541 }542543 Ok(when)544 }545546 fn place_task(547 when: T::BlockNumber,548 what: ScheduledOf<T>,549 ) -> Result<TaskAddress<T::BlockNumber>, (DispatchError, ScheduledOf<T>)> {550 let maybe_name = what.maybe_id;551 let index = Self::push_to_agenda(when, what)?;552 let address = (when, index);553 if let Some(name) = maybe_name {554 Lookup::<T>::insert(name, address)555 }556 Self::deposit_event(Event::Scheduled { when: address.0, index: address.1 });557 Ok(address)558 }559560 fn push_to_agenda(561 when: T::BlockNumber,562 what: ScheduledOf<T>,563 ) -> Result<u32, (DispatchError, ScheduledOf<T>)> {564 let mut agenda = Agenda::<T>::get(when);565 let index = if (agenda.len() as u32) < T::MaxScheduledPerBlock::get() {566 567 let _ = agenda.try_push(Some(what));568 agenda.len() as u32 - 1569 } else {570 if let Some(hole_index) = agenda.iter().position(|i| i.is_none()) {571 agenda[hole_index] = Some(what);572 hole_index as u32573 } else {574 return Err((<Error<T>>::AgendaIsExhausted.into(), what))575 }576 };577 Agenda::<T>::insert(when, agenda);578 Ok(index)579 }580581 fn do_schedule(582 when: DispatchTime<T::BlockNumber>,583 maybe_periodic: Option<schedule::Period<T::BlockNumber>>,584 priority: schedule::Priority,585 origin: T::PalletsOrigin,586 call: ScheduledCall<T>,587 ) -> Result<TaskAddress<T::BlockNumber>, DispatchError> {588 let when = Self::resolve_time(when)?;589590 591 let maybe_periodic = maybe_periodic592 .filter(|p| p.1 > 1 && !p.0.is_zero())593 594 .map(|(p, c)| (p, c - 1));595 let task = Scheduled {596 maybe_id: None,597 priority,598 call,599 maybe_periodic,600 origin,601 _phantom: PhantomData,602 };603 Self::place_task(when, task).map_err(|x| x.0)604 }605606 fn do_cancel(607 origin: Option<T::PalletsOrigin>,608 (when, index): TaskAddress<T::BlockNumber>,609 ) -> Result<(), DispatchError> {610 let scheduled = Agenda::<T>::try_mutate(when, |agenda| {611 agenda.get_mut(index as usize).map_or(612 Ok(None),613 |s| -> Result<Option<Scheduled<_, _, _, _, _>>, DispatchError> {614 if let (Some(ref o), Some(ref s)) = (origin, s.borrow()) {615 if matches!(616 T::OriginPrivilegeCmp::cmp_privilege(o, &s.origin),617 Some(Ordering::Less) | None618 ) {619 return Err(BadOrigin.into())620 }621 };622 Ok(s.take())623 },624 )625 })?;626 if let Some(s) = scheduled {627 T::Preimages::drop(&s.call);628629 if let Some(id) = s.maybe_id {630 Lookup::<T>::remove(id);631 }632 Self::deposit_event(Event::Canceled { when, index });633 Ok(())634 } else {635 return Err(Error::<T>::NotFound.into())636 }637 }638639 fn do_schedule_named(640 id: TaskName,641 when: DispatchTime<T::BlockNumber>,642 maybe_periodic: Option<schedule::Period<T::BlockNumber>>,643 priority: schedule::Priority,644 origin: T::PalletsOrigin,645 call: ScheduledCall<T>,646 ) -> Result<TaskAddress<T::BlockNumber>, DispatchError> {647 648 if Lookup::<T>::contains_key(&id) {649 return Err(Error::<T>::FailedToSchedule.into())650 }651652 let when = Self::resolve_time(when)?;653654 655 let maybe_periodic = maybe_periodic656 .filter(|p| p.1 > 1 && !p.0.is_zero())657 658 .map(|(p, c)| (p, c - 1));659660 let task = Scheduled {661 maybe_id: Some(id),662 priority,663 call,664 maybe_periodic,665 origin,666 _phantom: Default::default(),667 };668 Self::place_task(when, task).map_err(|x| x.0)669 }670671 fn do_cancel_named(origin: Option<T::PalletsOrigin>, id: TaskName) -> DispatchResult {672 Lookup::<T>::try_mutate_exists(id, |lookup| -> DispatchResult {673 if let Some((when, index)) = lookup.take() {674 let i = index as usize;675 Agenda::<T>::try_mutate(when, |agenda| -> DispatchResult {676 if let Some(s) = agenda.get_mut(i) {677 if let (Some(ref o), Some(ref s)) = (origin, s.borrow()) {678 if matches!(679 T::OriginPrivilegeCmp::cmp_privilege(o, &s.origin),680 Some(Ordering::Less) | None681 ) {682 return Err(BadOrigin.into())683 }684 T::Preimages::drop(&s.call);685 }686 *s = None;687 }688 Ok(())689 })?;690 Self::deposit_event(Event::Canceled { when, index });691 Ok(())692 } else {693 return Err(Error::<T>::NotFound.into())694 }695 })696 }697}698699enum ServiceTaskError {700 701 Unavailable,702 703 Overweight,704}705use ServiceTaskError::*;706707impl<T: Config> Pallet<T> {708 709 fn service_agendas(weight: &mut WeightCounter, now: T::BlockNumber, max: u32) {710 if !weight.check_accrue(T::WeightInfo::service_agendas_base()) {711 return712 }713714 let mut incomplete_since = now + One::one();715 let mut when = IncompleteSince::<T>::take().unwrap_or(now);716 let mut executed = 0;717718 let max_items = T::MaxScheduledPerBlock::get();719 let mut count_down = max;720 let service_agenda_base_weight = T::WeightInfo::service_agenda_base(max_items);721 while count_down > 0 && when <= now && weight.can_accrue(service_agenda_base_weight) {722 if !Self::service_agenda(weight, &mut executed, now, when, u32::max_value()) {723 incomplete_since = incomplete_since.min(when);724 }725 when.saturating_inc();726 count_down.saturating_dec();727 }728 incomplete_since = incomplete_since.min(when);729 if incomplete_since <= now {730 IncompleteSince::<T>::put(incomplete_since);731 }732 }733734 735 736 fn service_agenda(737 weight: &mut WeightCounter,738 executed: &mut u32,739 now: T::BlockNumber,740 when: T::BlockNumber,741 max: u32,742 ) -> bool {743 let mut agenda = Agenda::<T>::get(when);744 let mut ordered = agenda745 .iter()746 .enumerate()747 .filter_map(|(index, maybe_item)| {748 maybe_item.as_ref().map(|item| (index as u32, item.priority))749 })750 .collect::<Vec<_>>();751 ordered.sort_by_key(|k| k.1);752 let within_limit =753 weight.check_accrue(T::WeightInfo::service_agenda_base(ordered.len() as u32));754 debug_assert!(within_limit, "weight limit should have been checked in advance");755756 757 let mut postponed = (ordered.len() as u32).saturating_sub(max);758 759 let mut dropped = 0;760761 for (agenda_index, _) in ordered.into_iter().take(max as usize) {762 let task = match agenda[agenda_index as usize].take() {763 None => continue,764 Some(t) => t,765 };766 let base_weight = T::WeightInfo::service_task(767 task.call.lookup_len().map(|x| x as usize),768 task.maybe_id.is_some(),769 task.maybe_periodic.is_some(),770 );771 if !weight.can_accrue(base_weight) {772 postponed += 1;773 break774 }775 let result = Self::service_task(weight, now, when, agenda_index, *executed == 0, task);776 agenda[agenda_index as usize] = match result {777 Err((Unavailable, slot)) => {778 dropped += 1;779 slot780 },781 Err((Overweight, slot)) => {782 postponed += 1;783 slot784 },785 Ok(()) => {786 *executed += 1;787 None788 },789 };790 }791 if postponed > 0 || dropped > 0 {792 Agenda::<T>::insert(when, agenda);793 } else {794 Agenda::<T>::remove(when);795 }796 postponed == 0797 }798799 800 801 802 803 804 805 fn service_task(806 weight: &mut WeightCounter,807 now: T::BlockNumber,808 when: T::BlockNumber,809 agenda_index: u32,810 is_first: bool,811 mut task: ScheduledOf<T>,812 ) -> Result<(), (ServiceTaskError, Option<ScheduledOf<T>>)> {813 if let Some(ref id) = task.maybe_id {814 Lookup::<T>::remove(id);815 }816817 let (call, lookup_len) = match T::Preimages::peek(&task.call) {818 Ok(c) => c,819 Err(_) => return Err((Unavailable, Some(task))),820 };821822 weight.check_accrue(T::WeightInfo::service_task(823 lookup_len.map(|x| x as usize),824 task.maybe_id.is_some(),825 task.maybe_periodic.is_some(),826 ));827828 match Self::execute_dispatch(weight, task.origin.clone(), call) {829 Err(Unavailable) => {830 debug_assert!(false, "Checked to exist with `peek`");831 Self::deposit_event(Event::CallUnavailable {832 task: (when, agenda_index),833 id: task.maybe_id,834 });835 Err((Unavailable, Some(task)))836 },837 Err(Overweight) if is_first => {838 T::Preimages::drop(&task.call);839 Self::deposit_event(Event::PermanentlyOverweight {840 task: (when, agenda_index),841 id: task.maybe_id,842 });843 Err((Unavailable, Some(task)))844 },845 Err(Overweight) => Err((Overweight, Some(task))),846 Ok(result) => {847 Self::deposit_event(Event::Dispatched {848 task: (when, agenda_index),849 id: task.maybe_id,850 result,851 });852 if let &Some((period, count)) = &task.maybe_periodic {853 if count > 1 {854 task.maybe_periodic = Some((period, count - 1));855 } else {856 task.maybe_periodic = None;857 }858 let wake = now.saturating_add(period);859 match Self::place_task(wake, task) {860 Ok(_) => {},861 Err((_, task)) => {862 863 864 T::Preimages::drop(&task.call);865 Self::deposit_event(Event::PeriodicFailed {866 task: (when, agenda_index),867 id: task.maybe_id,868 });869 },870 }871 } else {872 T::Preimages::drop(&task.call);873 }874 Ok(())875 },876 }877 }878879 880 881 882 883 884 885 fn execute_dispatch(886 weight: &mut WeightCounter,887 origin: T::PalletsOrigin,888 call: <T as Config>::Call,889 ) -> Result<DispatchResult, ServiceTaskError> {890 let dispatch_origin: <T as Config>::Origin = origin.into();891 let base_weight = match dispatch_origin.clone().as_signed() {892 Some(_) => T::WeightInfo::execute_dispatch_signed(),893 _ => T::WeightInfo::execute_dispatch_unsigned(),894 };895 let call_weight = call.get_dispatch_info().weight;896 897 let max_weight = base_weight.saturating_add(call_weight);898899 if !weight.can_accrue(max_weight) {900 return Err(Overweight)901 }902903 let (maybe_actual_call_weight, result) = match call.dispatch(dispatch_origin) {904 Ok(post_info) => (post_info.actual_weight, Ok(())),905 Err(error_and_info) =>906 (error_and_info.post_info.actual_weight, Err(error_and_info.error)),907 };908 let call_weight = maybe_actual_call_weight.unwrap_or(call_weight);909 weight.check_accrue(base_weight);910 weight.check_accrue(call_weight);911 Ok(result)912 }913}