pezframe_support/
dispatch.rs

1// This file is part of Bizinikiwi.
2
3// Copyright (C) Parity Technologies (UK) Ltd. and Dijital Kurdistan Tech Institute
4// SPDX-License-Identifier: Apache-2.0
5
6// 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 at
9//
10// 	http://www.apache.org/licenses/LICENSE-2.0
11//
12// Unless required by applicable law or agreed to in writing, software
13// 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 and
16// limitations under the License.
17
18//! Dispatch system. Contains a macro for defining runtime modules and
19//! generating values representing lazy module function calls.
20
21use crate::traits::UnfilteredDispatchable;
22use codec::{Codec, Decode, DecodeWithMemTracking, Encode, EncodeLike, MaxEncodedLen};
23use core::fmt;
24use pezsp_runtime::{
25	generic::{CheckedExtrinsic, UncheckedExtrinsic},
26	traits::{
27		Dispatchable, ExtensionPostDispatchWeightHandler, RefundWeight, TransactionExtension,
28	},
29	DispatchError,
30};
31use pezsp_weights::Weight;
32use scale_info::TypeInfo;
33#[cfg(feature = "std")]
34use serde::{Deserialize, Serialize};
35
36/// The return type of a `Dispatchable` in frame. When returned explicitly from
37/// a dispatchable function it allows overriding the default `PostDispatchInfo`
38/// returned from a dispatch.
39pub type DispatchResultWithPostInfo = pezsp_runtime::DispatchResultWithInfo<PostDispatchInfo>;
40
41#[docify::export]
42/// Un-augmented version of `DispatchResultWithPostInfo` that can be returned from
43/// dispatchable functions and is automatically converted to the augmented type. Should be
44/// used whenever the `PostDispatchInfo` does not need to be overwritten. As this should
45/// be the common case it is the implicit return type when none is specified.
46pub type DispatchResult = Result<(), pezsp_runtime::DispatchError>;
47
48/// The error type contained in a `DispatchResultWithPostInfo`.
49pub type DispatchErrorWithPostInfo = pezsp_runtime::DispatchErrorWithPostInfo<PostDispatchInfo>;
50
51/// Serializable version of pezpallet dispatchable.
52pub trait Callable<T> {
53	type RuntimeCall: UnfilteredDispatchable + Codec + Clone + PartialEq + Eq;
54}
55
56// dirty hack to work around serde_derive issue
57// https://github.com/rust-lang/rust/issues/51331
58pub type CallableCallFor<A, R> = <A as Callable<R>>::RuntimeCall;
59
60/// Means to checks if the dispatchable is feeless.
61///
62/// This is automatically implemented for all dispatchables during pezpallet expansion.
63/// If a call is marked by
64/// [`#[pezpallet::feeless_if]`](`macro@pezframe_support_procedural::feeless_if`) attribute, the
65/// corresponding closure is checked.
66pub trait CheckIfFeeless {
67	/// The Origin type of the runtime.
68	type Origin;
69
70	/// Checks if the dispatchable satisfies the feeless condition as defined by
71	/// [`#[pezpallet::feeless_if]`](`macro@pezframe_support_procedural::feeless_if`)
72	fn is_feeless(&self, origin: &Self::Origin) -> bool;
73}
74
75/// Origin for the System pezpallet.
76#[derive(
77	PartialEq, Eq, Clone, Debug, Encode, Decode, DecodeWithMemTracking, TypeInfo, MaxEncodedLen,
78)]
79pub enum RawOrigin<AccountId> {
80	/// The system itself ordained this dispatch to happen: this is the highest privilege level.
81	Root,
82	/// It is signed by some public key and we provide the `AccountId`.
83	Signed(AccountId),
84	/// It is signed by nobody, can be either:
85	/// * included and agreed upon by the validators anyway,
86	/// * or unsigned transaction validated by a pezpallet.
87	None,
88	/// It is signed by nobody, the extrinsic is authorized by the runtime.
89	///
90	/// Authorization logic is defined by pallets.
91	/// See trait [`Authorize`](crate::traits::Authorize) and attribute macro
92	/// [`authorize`](crate::pezpallet_macros::authorize) for more details.
93	Authorized,
94}
95
96impl<AccountId> From<Option<AccountId>> for RawOrigin<AccountId> {
97	fn from(s: Option<AccountId>) -> RawOrigin<AccountId> {
98		match s {
99			Some(who) => RawOrigin::Signed(who),
100			None => RawOrigin::None,
101		}
102	}
103}
104
105impl<AccountId> RawOrigin<AccountId> {
106	/// Returns `Some` with a reference to the `AccountId` if `self` is `Signed`, `None` otherwise.
107	pub fn as_signed(&self) -> Option<&AccountId> {
108		match &self {
109			Self::Signed(x) => Some(x),
110			_ => None,
111		}
112	}
113
114	/// Returns `true` if `self` is `Root`, `None` otherwise.
115	pub fn is_root(&self) -> bool {
116		matches!(&self, Self::Root)
117	}
118
119	/// Returns `true` if `self` is `None`, `None` otherwise.
120	pub fn is_none(&self) -> bool {
121		matches!(&self, Self::None)
122	}
123}
124
125/// A type that can be used as a parameter in a dispatchable function.
126///
127/// When using `decl_module` all arguments for call functions must implement this trait.
128pub trait Parameter:
129	Codec + DecodeWithMemTracking + EncodeLike + Clone + Eq + fmt::Debug + scale_info::TypeInfo
130{
131}
132impl<T> Parameter for T where
133	T: Codec + DecodeWithMemTracking + EncodeLike + Clone + Eq + fmt::Debug + scale_info::TypeInfo
134{
135}
136
137/// Means of classifying a dispatchable function.
138pub trait ClassifyDispatch<T> {
139	/// Classify the dispatch function based on input data `target` of type `T`. When implementing
140	/// this for a dispatchable, `T` will be a tuple of all arguments given to the function (except
141	/// origin).
142	fn classify_dispatch(&self, target: T) -> DispatchClass;
143}
144
145/// Indicates if dispatch function should pay fees or not.
146///
147/// If set to `Pays::No`, the block resource limits are applied, yet no fee is deducted.
148pub trait PaysFee<T> {
149	fn pays_fee(&self, _target: T) -> Pays;
150}
151
152/// Explicit enum to denote if a transaction pays fee or not.
153#[derive(Clone, Copy, Eq, PartialEq, Debug, Encode, Decode, DecodeWithMemTracking, TypeInfo)]
154pub enum Pays {
155	/// Transactor will pay related fees.
156	Yes,
157	/// Transactor will NOT pay related fees.
158	No,
159}
160
161impl Default for Pays {
162	fn default() -> Self {
163		Self::Yes
164	}
165}
166
167impl From<Pays> for PostDispatchInfo {
168	fn from(pays_fee: Pays) -> Self {
169		Self { actual_weight: None, pays_fee }
170	}
171}
172
173impl From<bool> for Pays {
174	fn from(b: bool) -> Self {
175		match b {
176			true => Self::Yes,
177			false => Self::No,
178		}
179	}
180}
181
182/// A generalized group of dispatch types.
183///
184/// NOTE whenever upgrading the enum make sure to also update
185/// [DispatchClass::all] and [DispatchClass::non_mandatory] helper functions.
186#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
187#[cfg_attr(feature = "std", serde(rename_all = "camelCase"))]
188#[derive(PartialEq, Eq, Clone, Copy, Encode, Decode, DecodeWithMemTracking, Debug, TypeInfo)]
189pub enum DispatchClass {
190	/// A normal dispatch.
191	Normal,
192	/// An operational dispatch.
193	Operational,
194	/// A mandatory dispatch. These kinds of dispatch are always included regardless of their
195	/// weight, therefore it is critical that they are separately validated to ensure that a
196	/// malicious validator cannot craft a valid but impossibly heavy block. Usually this just
197	/// means ensuring that the extrinsic can only be included once and that it is always very
198	/// light.
199	///
200	/// Do *NOT* use it for extrinsics that can be heavy.
201	///
202	/// The only real use case for this is inherent extrinsics that are required to execute in a
203	/// block for the block to be valid, and it solves the issue in the case that the block
204	/// initialization is sufficiently heavy to mean that those inherents do not fit into the
205	/// block. Essentially, we assume that in these exceptional circumstances, it is better to
206	/// allow an overweight block to be created than to not allow any block at all to be created.
207	Mandatory,
208}
209
210impl Default for DispatchClass {
211	fn default() -> Self {
212		Self::Normal
213	}
214}
215
216impl DispatchClass {
217	/// Returns an array containing all dispatch classes.
218	pub fn all() -> &'static [DispatchClass] {
219		&[DispatchClass::Normal, DispatchClass::Operational, DispatchClass::Mandatory]
220	}
221
222	/// Returns an array of all dispatch classes except `Mandatory`.
223	pub fn non_mandatory() -> &'static [DispatchClass] {
224		&[DispatchClass::Normal, DispatchClass::Operational]
225	}
226}
227
228/// A trait that represents one or many values of given type.
229///
230/// Useful to accept as parameter type to let the caller pass either a single value directly
231/// or an iterator.
232pub trait OneOrMany<T> {
233	/// The iterator type.
234	type Iter: Iterator<Item = T>;
235	/// Convert this item into an iterator.
236	fn into_iter(self) -> Self::Iter;
237}
238
239impl OneOrMany<DispatchClass> for DispatchClass {
240	type Iter = core::iter::Once<DispatchClass>;
241	fn into_iter(self) -> Self::Iter {
242		core::iter::once(self)
243	}
244}
245
246impl<'a> OneOrMany<DispatchClass> for &'a [DispatchClass] {
247	type Iter = core::iter::Cloned<core::slice::Iter<'a, DispatchClass>>;
248	fn into_iter(self) -> Self::Iter {
249		self.iter().cloned()
250	}
251}
252
253/// A bundle of static information collected from the `#[pezpallet::weight]` attributes.
254#[derive(Clone, Copy, Eq, PartialEq, Default, Debug, Encode, Decode, TypeInfo)]
255pub struct DispatchInfo {
256	/// Weight of this transaction's call.
257	pub call_weight: Weight,
258	/// Weight of this transaction's extension.
259	pub extension_weight: Weight,
260	/// Class of this transaction.
261	pub class: DispatchClass,
262	/// Does this transaction pay fees.
263	pub pays_fee: Pays,
264}
265
266impl DispatchInfo {
267	/// Returns the weight used by this extrinsic's extension and call when applied.
268	pub fn total_weight(&self) -> Weight {
269		self.call_weight.saturating_add(self.extension_weight)
270	}
271}
272
273/// A `Dispatchable` function (aka transaction) that can carry some static information along with
274/// it, using the `#[pezpallet::weight]` attribute.
275pub trait GetDispatchInfo {
276	/// Return a `DispatchInfo`, containing relevant information of this dispatch.
277	///
278	/// This is done independently of its encoded size.
279	fn get_dispatch_info(&self) -> DispatchInfo;
280}
281
282impl GetDispatchInfo for () {
283	fn get_dispatch_info(&self) -> DispatchInfo {
284		DispatchInfo::default()
285	}
286}
287
288/// Extract the actual weight from a dispatch result if any or fall back to the default weight.
289pub fn extract_actual_weight(result: &DispatchResultWithPostInfo, info: &DispatchInfo) -> Weight {
290	match result {
291		Ok(post_info) => post_info,
292		Err(err) => &err.post_info,
293	}
294	.calc_actual_weight(info)
295}
296
297/// Extract the actual pays_fee from a dispatch result if any or fall back to the default
298/// weight.
299pub fn extract_actual_pays_fee(result: &DispatchResultWithPostInfo, info: &DispatchInfo) -> Pays {
300	match result {
301		Ok(post_info) => post_info,
302		Err(err) => &err.post_info,
303	}
304	.pays_fee(info)
305}
306
307/// Weight information that is only available post dispatch.
308/// NOTE: This can only be used to reduce the weight or fee, not increase it.
309#[derive(
310	Clone, Copy, Eq, PartialEq, Default, Debug, Encode, Decode, DecodeWithMemTracking, TypeInfo,
311)]
312pub struct PostDispatchInfo {
313	/// Actual weight consumed by a call or `None` which stands for the worst case static weight.
314	pub actual_weight: Option<Weight>,
315	/// Whether this transaction should pay fees when all is said and done.
316	pub pays_fee: Pays,
317}
318
319impl PostDispatchInfo {
320	/// Calculate how much (if any) weight was not used by the `Dispatchable`.
321	pub fn calc_unspent(&self, info: &DispatchInfo) -> Weight {
322		info.total_weight() - self.calc_actual_weight(info)
323	}
324
325	/// Calculate how much weight was actually spent by the `Dispatchable`.
326	pub fn calc_actual_weight(&self, info: &DispatchInfo) -> Weight {
327		if let Some(actual_weight) = self.actual_weight {
328			let info_total_weight = info.total_weight();
329			if actual_weight.any_gt(info_total_weight) {
330				log::error!(
331					target: crate::LOG_TARGET,
332					"Post dispatch weight is greater than pre dispatch weight. \
333					Pre dispatch weight may underestimating the actual weight. \
334					Greater post dispatch weight components are ignored.
335					Pre dispatch weight: {info_total_weight:?},
336					Post dispatch weight: {actual_weight:?}",
337				);
338			}
339			actual_weight.min(info.total_weight())
340		} else {
341			info.total_weight()
342		}
343	}
344
345	/// Determine if user should actually pay fees at the end of the dispatch.
346	pub fn pays_fee(&self, info: &DispatchInfo) -> Pays {
347		// If they originally were not paying fees, or the post dispatch info
348		// says they should not pay fees, then they don't pay fees.
349		// This is because the pre dispatch information must contain the
350		// worst case for weight and fees paid.
351		if info.pays_fee == Pays::No || self.pays_fee == Pays::No {
352			Pays::No
353		} else {
354			// Otherwise they pay.
355			Pays::Yes
356		}
357	}
358}
359
360impl From<()> for PostDispatchInfo {
361	fn from(_: ()) -> Self {
362		Self { actual_weight: None, pays_fee: Default::default() }
363	}
364}
365
366impl pezsp_runtime::traits::Printable for PostDispatchInfo {
367	fn print(&self) {
368		"actual_weight=".print();
369		match self.actual_weight {
370			Some(weight) => weight.print(),
371			None => "max-weight".print(),
372		};
373		"pays_fee=".print();
374		match self.pays_fee {
375			Pays::Yes => "Yes".print(),
376			Pays::No => "No".print(),
377		}
378	}
379}
380
381/// Allows easy conversion from `DispatchError` to `DispatchErrorWithPostInfo` for dispatchables
382/// that want to return a custom a posterior weight on error.
383pub trait WithPostDispatchInfo {
384	/// Call this on your modules custom errors type in order to return a custom weight on error.
385	///
386	/// # Example
387	///
388	/// ```ignore
389	/// let who = ensure_signed(origin).map_err(|e| e.with_weight(Weight::from_parts(100, 0)))?;
390	/// ensure!(who == me, Error::<T>::NotMe.with_weight(200_000));
391	/// ```
392	fn with_weight(self, actual_weight: Weight) -> DispatchErrorWithPostInfo;
393}
394
395impl<T> WithPostDispatchInfo for T
396where
397	T: Into<DispatchError>,
398{
399	fn with_weight(self, actual_weight: Weight) -> DispatchErrorWithPostInfo {
400		DispatchErrorWithPostInfo {
401			post_info: PostDispatchInfo {
402				actual_weight: Some(actual_weight),
403				pays_fee: Default::default(),
404			},
405			error: self.into(),
406		}
407	}
408}
409
410/// Implementation for unchecked extrinsic.
411impl<Address, Call: Dispatchable, Signature, Extension: TransactionExtension<Call>> GetDispatchInfo
412	for UncheckedExtrinsic<Address, Call, Signature, Extension>
413where
414	Call: GetDispatchInfo + Dispatchable,
415{
416	fn get_dispatch_info(&self) -> DispatchInfo {
417		let mut info = self.function.get_dispatch_info();
418		info.extension_weight = self.extension_weight();
419		info
420	}
421}
422
423/// Implementation for checked extrinsic.
424impl<AccountId, Call: Dispatchable, Extension: TransactionExtension<Call>> GetDispatchInfo
425	for CheckedExtrinsic<AccountId, Call, Extension>
426where
427	Call: GetDispatchInfo,
428{
429	fn get_dispatch_info(&self) -> DispatchInfo {
430		let mut info = self.function.get_dispatch_info();
431		info.extension_weight = self.extension_weight();
432		info
433	}
434}
435
436/// A struct holding value for each `DispatchClass`.
437#[derive(Clone, Eq, PartialEq, Default, Debug, Encode, Decode, TypeInfo, MaxEncodedLen)]
438pub struct PerDispatchClass<T> {
439	/// Value for `Normal` extrinsics.
440	normal: T,
441	/// Value for `Operational` extrinsics.
442	operational: T,
443	/// Value for `Mandatory` extrinsics.
444	mandatory: T,
445}
446
447impl<T> PerDispatchClass<T> {
448	/// Create new `PerDispatchClass` with the same value for every class.
449	pub fn new(val: impl Fn(DispatchClass) -> T) -> Self {
450		Self {
451			normal: val(DispatchClass::Normal),
452			operational: val(DispatchClass::Operational),
453			mandatory: val(DispatchClass::Mandatory),
454		}
455	}
456
457	/// Get a mutable reference to current value of given class.
458	pub fn get_mut(&mut self, class: DispatchClass) -> &mut T {
459		match class {
460			DispatchClass::Operational => &mut self.operational,
461			DispatchClass::Normal => &mut self.normal,
462			DispatchClass::Mandatory => &mut self.mandatory,
463		}
464	}
465
466	/// Get current value for given class.
467	pub fn get(&self, class: DispatchClass) -> &T {
468		match class {
469			DispatchClass::Normal => &self.normal,
470			DispatchClass::Operational => &self.operational,
471			DispatchClass::Mandatory => &self.mandatory,
472		}
473	}
474}
475
476impl<T: Clone> PerDispatchClass<T> {
477	/// Set the value of given class.
478	pub fn set(&mut self, new: T, class: impl OneOrMany<DispatchClass>) {
479		for class in class.into_iter() {
480			*self.get_mut(class) = new.clone();
481		}
482	}
483}
484
485impl PerDispatchClass<Weight> {
486	/// Returns the total weight consumed by all extrinsics in the block.
487	///
488	/// Saturates on overflow.
489	pub fn total(&self) -> Weight {
490		let mut sum = Weight::zero();
491		for class in DispatchClass::all() {
492			sum.saturating_accrue(*self.get(*class));
493		}
494		sum
495	}
496
497	/// Add some weight to the given class. Saturates at the numeric bounds.
498	pub fn add(mut self, weight: Weight, class: DispatchClass) -> Self {
499		self.accrue(weight, class);
500		self
501	}
502
503	/// Increase the weight of the given class. Saturates at the numeric bounds.
504	pub fn accrue(&mut self, weight: Weight, class: DispatchClass) {
505		self.get_mut(class).saturating_accrue(weight);
506	}
507
508	/// Try to increase the weight of the given class. Saturates at the numeric bounds.
509	pub fn checked_accrue(&mut self, weight: Weight, class: DispatchClass) -> Result<(), ()> {
510		self.get_mut(class).checked_accrue(weight).ok_or(())
511	}
512
513	/// Reduce the weight of the given class. Saturates at the numeric bounds.
514	pub fn reduce(&mut self, weight: Weight, class: DispatchClass) {
515		self.get_mut(class).saturating_reduce(weight);
516	}
517}
518
519/// Means of weighing some particular kind of data (`T`).
520pub trait WeighData<T> {
521	/// Weigh the data `T` given by `target`. When implementing this for a dispatchable, `T` will be
522	/// a tuple of all arguments given to the function (except origin).
523	fn weigh_data(&self, target: T) -> Weight;
524}
525
526impl<T> WeighData<T> for Weight {
527	fn weigh_data(&self, _: T) -> Weight {
528		return *self;
529	}
530}
531
532impl<T> PaysFee<T> for (Weight, DispatchClass, Pays) {
533	fn pays_fee(&self, _: T) -> Pays {
534		self.2
535	}
536}
537
538impl<T> WeighData<T> for (Weight, DispatchClass) {
539	fn weigh_data(&self, args: T) -> Weight {
540		return self.0.weigh_data(args);
541	}
542}
543
544impl<T> WeighData<T> for (Weight, DispatchClass, Pays) {
545	fn weigh_data(&self, args: T) -> Weight {
546		return self.0.weigh_data(args);
547	}
548}
549
550impl<T> ClassifyDispatch<T> for (Weight, DispatchClass) {
551	fn classify_dispatch(&self, _: T) -> DispatchClass {
552		self.1
553	}
554}
555
556impl<T> PaysFee<T> for (Weight, DispatchClass) {
557	fn pays_fee(&self, _: T) -> Pays {
558		Pays::Yes
559	}
560}
561
562impl<T> WeighData<T> for (Weight, Pays) {
563	fn weigh_data(&self, args: T) -> Weight {
564		return self.0.weigh_data(args);
565	}
566}
567
568impl<T> ClassifyDispatch<T> for (Weight, Pays) {
569	fn classify_dispatch(&self, _: T) -> DispatchClass {
570		DispatchClass::Normal
571	}
572}
573
574impl<T> PaysFee<T> for (Weight, Pays) {
575	fn pays_fee(&self, _: T) -> Pays {
576		self.1
577	}
578}
579
580impl From<(Option<Weight>, Pays)> for PostDispatchInfo {
581	fn from(post_weight_info: (Option<Weight>, Pays)) -> Self {
582		let (actual_weight, pays_fee) = post_weight_info;
583		Self { actual_weight, pays_fee }
584	}
585}
586
587impl From<Option<Weight>> for PostDispatchInfo {
588	fn from(actual_weight: Option<Weight>) -> Self {
589		Self { actual_weight, pays_fee: Default::default() }
590	}
591}
592
593impl<T> ClassifyDispatch<T> for Weight {
594	fn classify_dispatch(&self, _: T) -> DispatchClass {
595		DispatchClass::Normal
596	}
597}
598
599impl<T> PaysFee<T> for Weight {
600	fn pays_fee(&self, _: T) -> Pays {
601		Pays::Yes
602	}
603}
604
605impl<T> ClassifyDispatch<T> for (Weight, DispatchClass, Pays) {
606	fn classify_dispatch(&self, _: T) -> DispatchClass {
607		self.1
608	}
609}
610
611impl RefundWeight for PostDispatchInfo {
612	fn refund(&mut self, weight: Weight) {
613		if let Some(actual_weight) = self.actual_weight.as_mut() {
614			actual_weight.saturating_reduce(weight);
615		}
616	}
617}
618
619impl ExtensionPostDispatchWeightHandler<DispatchInfo> for PostDispatchInfo {
620	fn set_extension_weight(&mut self, info: &DispatchInfo) {
621		let actual_weight = self
622			.actual_weight
623			.unwrap_or(info.call_weight)
624			.saturating_add(info.extension_weight);
625		self.actual_weight = Some(actual_weight);
626	}
627}
628
629impl ExtensionPostDispatchWeightHandler<()> for PostDispatchInfo {
630	fn set_extension_weight(&mut self, _: &()) {}
631}
632
633// TODO: Eventually remove these
634
635impl<T> ClassifyDispatch<T> for u64 {
636	fn classify_dispatch(&self, _: T) -> DispatchClass {
637		DispatchClass::Normal
638	}
639}
640
641impl<T> PaysFee<T> for u64 {
642	fn pays_fee(&self, _: T) -> Pays {
643		Pays::Yes
644	}
645}
646
647impl<T> WeighData<T> for u64 {
648	fn weigh_data(&self, _: T) -> Weight {
649		return Weight::from_parts(*self, 0);
650	}
651}
652
653impl<T> WeighData<T> for (u64, DispatchClass, Pays) {
654	fn weigh_data(&self, args: T) -> Weight {
655		return self.0.weigh_data(args);
656	}
657}
658
659impl<T> ClassifyDispatch<T> for (u64, DispatchClass, Pays) {
660	fn classify_dispatch(&self, _: T) -> DispatchClass {
661		self.1
662	}
663}
664
665impl<T> PaysFee<T> for (u64, DispatchClass, Pays) {
666	fn pays_fee(&self, _: T) -> Pays {
667		self.2
668	}
669}
670
671impl<T> WeighData<T> for (u64, DispatchClass) {
672	fn weigh_data(&self, args: T) -> Weight {
673		return self.0.weigh_data(args);
674	}
675}
676
677impl<T> ClassifyDispatch<T> for (u64, DispatchClass) {
678	fn classify_dispatch(&self, _: T) -> DispatchClass {
679		self.1
680	}
681}
682
683impl<T> PaysFee<T> for (u64, DispatchClass) {
684	fn pays_fee(&self, _: T) -> Pays {
685		Pays::Yes
686	}
687}
688
689impl<T> WeighData<T> for (u64, Pays) {
690	fn weigh_data(&self, args: T) -> Weight {
691		return self.0.weigh_data(args);
692	}
693}
694
695impl<T> ClassifyDispatch<T> for (u64, Pays) {
696	fn classify_dispatch(&self, _: T) -> DispatchClass {
697		DispatchClass::Normal
698	}
699}
700
701impl<T> PaysFee<T> for (u64, Pays) {
702	fn pays_fee(&self, _: T) -> Pays {
703		self.1
704	}
705}
706
707// END TODO
708
709#[cfg(test)]
710// Do not complain about unused `dispatch` and `dispatch_aux`.
711#[allow(dead_code)]
712mod weight_tests {
713	use super::*;
714	use pezsp_core::parameter_types;
715	use pezsp_runtime::{generic, traits::BlakeTwo256};
716	use pezsp_weights::RuntimeDbWeight;
717
718	pub use self::pezframe_system::{Call, Config};
719
720	fn from_actual_ref_time(ref_time: Option<u64>) -> PostDispatchInfo {
721		PostDispatchInfo {
722			actual_weight: ref_time.map(|t| Weight::from_all(t)),
723			pays_fee: Default::default(),
724		}
725	}
726
727	fn from_post_weight_info(ref_time: Option<u64>, pays_fee: Pays) -> PostDispatchInfo {
728		PostDispatchInfo { actual_weight: ref_time.map(|t| Weight::from_all(t)), pays_fee }
729	}
730
731	#[crate::pezpallet(dev_mode)]
732	pub mod pezframe_system {
733		use super::{pezframe_system, pezframe_system::pezpallet_prelude::*};
734		pub use crate::dispatch::RawOrigin;
735		use crate::pezpallet_prelude::*;
736
737		#[pezpallet::pezpallet]
738		pub struct Pezpallet<T>(_);
739
740		#[pezpallet::config]
741		#[pezpallet::disable_pezframe_system_supertrait_check]
742		pub trait Config: 'static {
743			type Block: Parameter + pezsp_runtime::traits::Block;
744			type AccountId;
745			type Balance;
746			type BaseCallFilter: crate::traits::Contains<Self::RuntimeCall>;
747			type RuntimeOrigin;
748			type RuntimeCall;
749			type RuntimeTask;
750			type PalletInfo: crate::traits::PalletInfo;
751			type DbWeight: Get<crate::weights::RuntimeDbWeight>;
752		}
753
754		#[pezpallet::error]
755		pub enum Error<T> {
756			/// Required by construct_runtime
757			CallFiltered,
758		}
759
760		#[pezpallet::origin]
761		pub type Origin<T> = RawOrigin<<T as Config>::AccountId>;
762
763		#[pezpallet::call]
764		impl<T: Config> Pezpallet<T> {
765			// no arguments, fixed weight
766			#[pezpallet::weight(1000)]
767			pub fn f00(_origin: OriginFor<T>) -> DispatchResult {
768				unimplemented!();
769			}
770
771			#[pezpallet::weight((1000, DispatchClass::Mandatory))]
772			pub fn f01(_origin: OriginFor<T>) -> DispatchResult {
773				unimplemented!();
774			}
775
776			#[pezpallet::weight((1000, Pays::No))]
777			pub fn f02(_origin: OriginFor<T>) -> DispatchResult {
778				unimplemented!();
779			}
780
781			#[pezpallet::weight((1000, DispatchClass::Operational, Pays::No))]
782			pub fn f03(_origin: OriginFor<T>) -> DispatchResult {
783				unimplemented!();
784			}
785
786			// weight = a x 10 + b
787			#[pezpallet::weight(((_a * 10 + _eb * 1) as u64, DispatchClass::Normal, Pays::Yes))]
788			pub fn f11(_origin: OriginFor<T>, _a: u32, _eb: u32) -> DispatchResult {
789				unimplemented!();
790			}
791
792			#[pezpallet::weight((0, DispatchClass::Operational, Pays::Yes))]
793			pub fn f12(_origin: OriginFor<T>, _a: u32, _eb: u32) -> DispatchResult {
794				unimplemented!();
795			}
796
797			#[pezpallet::weight(T::DbWeight::get().reads(3) + T::DbWeight::get().writes(2) + Weight::from_all(10_000))]
798			pub fn f20(_origin: OriginFor<T>) -> DispatchResult {
799				unimplemented!();
800			}
801
802			#[pezpallet::weight(T::DbWeight::get().reads_writes(6, 5) + Weight::from_all(40_000))]
803			pub fn f21(_origin: OriginFor<T>) -> DispatchResult {
804				unimplemented!();
805			}
806
807			#[pezpallet::weight(1000)]
808			pub fn f99(_origin: OriginFor<T>) -> DispatchResult {
809				Ok(())
810			}
811
812			#[pezpallet::weight(1000)]
813			pub fn f100(_origin: OriginFor<T>) -> DispatchResultWithPostInfo {
814				Ok(crate::dispatch::PostDispatchInfo {
815					actual_weight: Some(Weight::from_parts(500, 0)),
816					pays_fee: Pays::Yes,
817				})
818			}
819		}
820
821		pub mod pezpallet_prelude {
822			pub type OriginFor<T> = <T as super::Config>::RuntimeOrigin;
823
824			pub type HeaderFor<T> =
825				<<T as super::Config>::Block as pezsp_runtime::traits::HeaderProvider>::HeaderT;
826
827			pub type BlockNumberFor<T> = <HeaderFor<T> as pezsp_runtime::traits::Header>::Number;
828		}
829	}
830
831	type BlockNumber = u32;
832	type AccountId = u32;
833	type Balance = u32;
834	type Header = generic::Header<BlockNumber, BlakeTwo256>;
835	type UncheckedExtrinsic = generic::UncheckedExtrinsic<u32, RuntimeCall, (), ()>;
836	type Block = generic::Block<Header, UncheckedExtrinsic>;
837
838	crate::construct_runtime!(
839		pub enum Runtime
840		{
841			System: self::pezframe_system,
842		}
843	);
844
845	parameter_types! {
846		pub const DbWeight: RuntimeDbWeight = RuntimeDbWeight {
847			read: 100,
848			write: 1000,
849		};
850	}
851
852	impl Config for Runtime {
853		type Block = Block;
854		type AccountId = AccountId;
855		type Balance = Balance;
856		type BaseCallFilter = crate::traits::Everything;
857		type RuntimeOrigin = RuntimeOrigin;
858		type RuntimeCall = RuntimeCall;
859		type RuntimeTask = RuntimeTask;
860		type DbWeight = DbWeight;
861		type PalletInfo = PalletInfo;
862	}
863
864	#[test]
865	fn weights_are_correct() {
866		// #[pezpallet::weight(1000)]
867		let info = Call::<Runtime>::f00 {}.get_dispatch_info();
868		assert_eq!(info.total_weight(), Weight::from_parts(1000, 0));
869		assert_eq!(info.class, DispatchClass::Normal);
870		assert_eq!(info.pays_fee, Pays::Yes);
871
872		// #[pezpallet::weight((1000, DispatchClass::Mandatory))]
873		let info = Call::<Runtime>::f01 {}.get_dispatch_info();
874		assert_eq!(info.total_weight(), Weight::from_parts(1000, 0));
875		assert_eq!(info.class, DispatchClass::Mandatory);
876		assert_eq!(info.pays_fee, Pays::Yes);
877
878		// #[pezpallet::weight((1000, Pays::No))]
879		let info = Call::<Runtime>::f02 {}.get_dispatch_info();
880		assert_eq!(info.total_weight(), Weight::from_parts(1000, 0));
881		assert_eq!(info.class, DispatchClass::Normal);
882		assert_eq!(info.pays_fee, Pays::No);
883
884		// #[pezpallet::weight((1000, DispatchClass::Operational, Pays::No))]
885		let info = Call::<Runtime>::f03 {}.get_dispatch_info();
886		assert_eq!(info.total_weight(), Weight::from_parts(1000, 0));
887		assert_eq!(info.class, DispatchClass::Operational);
888		assert_eq!(info.pays_fee, Pays::No);
889
890		// #[pezpallet::weight(((_a * 10 + _eb * 1) as u64, DispatchClass::Normal, Pays::Yes))]
891		let info = Call::<Runtime>::f11 { a: 13, eb: 20 }.get_dispatch_info();
892		assert_eq!(info.total_weight(), Weight::from_parts(150, 0)); // 13*10 + 20
893		assert_eq!(info.class, DispatchClass::Normal);
894		assert_eq!(info.pays_fee, Pays::Yes);
895
896		// #[pezpallet::weight((0, DispatchClass::Operational, Pays::Yes))]
897		let info = Call::<Runtime>::f12 { a: 10, eb: 20 }.get_dispatch_info();
898		assert_eq!(info.total_weight(), Weight::zero());
899		assert_eq!(info.class, DispatchClass::Operational);
900		assert_eq!(info.pays_fee, Pays::Yes);
901
902		// #[pezpallet::weight(T::DbWeight::get().reads(3) + T::DbWeight::get().writes(2) +
903		// Weight::from_all(10_000))]
904		let info = Call::<Runtime>::f20 {}.get_dispatch_info();
905		assert_eq!(info.total_weight(), Weight::from_parts(12300, 10000)); // 100*3 + 1000*2 + 10_1000
906		assert_eq!(info.class, DispatchClass::Normal);
907		assert_eq!(info.pays_fee, Pays::Yes);
908
909		// #[pezpallet::weight(T::DbWeight::get().reads_writes(6, 5) + Weight::from_all(40_000))]
910		let info = Call::<Runtime>::f21 {}.get_dispatch_info();
911		assert_eq!(info.total_weight(), Weight::from_parts(45600, 40000)); // 100*6 + 1000*5 + 40_1000
912		assert_eq!(info.class, DispatchClass::Normal);
913		assert_eq!(info.pays_fee, Pays::Yes);
914	}
915
916	#[test]
917	fn extract_actual_weight_works() {
918		let pre = DispatchInfo {
919			call_weight: Weight::from_parts(1000, 0),
920			extension_weight: Weight::zero(),
921			..Default::default()
922		};
923		assert_eq!(
924			extract_actual_weight(&Ok(from_actual_ref_time(Some(7))), &pre),
925			Weight::from_parts(7, 0)
926		);
927		assert_eq!(
928			extract_actual_weight(&Ok(from_actual_ref_time(Some(1000))), &pre),
929			Weight::from_parts(1000, 0)
930		);
931		assert_eq!(
932			extract_actual_weight(
933				&Err(DispatchError::BadOrigin.with_weight(Weight::from_parts(9, 0))),
934				&pre
935			),
936			Weight::from_parts(9, 0)
937		);
938	}
939
940	#[test]
941	fn extract_actual_weight_caps_at_pre_weight() {
942		let pre = DispatchInfo {
943			call_weight: Weight::from_parts(1000, 0),
944			extension_weight: Weight::zero(),
945			..Default::default()
946		};
947		assert_eq!(
948			extract_actual_weight(&Ok(from_actual_ref_time(Some(1250))), &pre),
949			Weight::from_parts(1000, 0)
950		);
951		assert_eq!(
952			extract_actual_weight(
953				&Err(DispatchError::BadOrigin.with_weight(Weight::from_parts(1300, 0))),
954				&pre
955			),
956			Weight::from_parts(1000, 0),
957		);
958	}
959
960	#[test]
961	fn extract_actual_pays_fee_works() {
962		let pre = DispatchInfo {
963			call_weight: Weight::from_parts(1000, 0),
964			extension_weight: Weight::zero(),
965			..Default::default()
966		};
967		assert_eq!(extract_actual_pays_fee(&Ok(from_actual_ref_time(Some(7))), &pre), Pays::Yes);
968		assert_eq!(
969			extract_actual_pays_fee(&Ok(from_actual_ref_time(Some(1000)).into()), &pre),
970			Pays::Yes
971		);
972		assert_eq!(
973			extract_actual_pays_fee(&Ok(from_post_weight_info(Some(1000), Pays::Yes)), &pre),
974			Pays::Yes
975		);
976		assert_eq!(
977			extract_actual_pays_fee(&Ok(from_post_weight_info(Some(1000), Pays::No)), &pre),
978			Pays::No
979		);
980		assert_eq!(
981			extract_actual_pays_fee(
982				&Err(DispatchError::BadOrigin.with_weight(Weight::from_parts(9, 0))),
983				&pre
984			),
985			Pays::Yes
986		);
987		assert_eq!(
988			extract_actual_pays_fee(
989				&Err(DispatchErrorWithPostInfo {
990					post_info: PostDispatchInfo { actual_weight: None, pays_fee: Pays::No },
991					error: DispatchError::BadOrigin,
992				}),
993				&pre
994			),
995			Pays::No
996		);
997
998		let pre = DispatchInfo {
999			call_weight: Weight::from_parts(1000, 0),
1000			extension_weight: Weight::zero(),
1001			pays_fee: Pays::No,
1002			..Default::default()
1003		};
1004		assert_eq!(extract_actual_pays_fee(&Ok(from_actual_ref_time(Some(7))), &pre), Pays::No);
1005		assert_eq!(extract_actual_pays_fee(&Ok(from_actual_ref_time(Some(1000))), &pre), Pays::No);
1006		assert_eq!(
1007			extract_actual_pays_fee(&Ok(from_post_weight_info(Some(1000), Pays::Yes)), &pre),
1008			Pays::No
1009		);
1010	}
1011
1012	#[test]
1013	fn weight_accrue_works() {
1014		let mut post_dispatch = PostDispatchInfo {
1015			actual_weight: Some(Weight::from_parts(1100, 25)),
1016			pays_fee: Pays::Yes,
1017		};
1018		post_dispatch.refund(Weight::from_parts(100, 15));
1019		assert_eq!(
1020			post_dispatch,
1021			PostDispatchInfo {
1022				actual_weight: Some(Weight::from_parts(1000, 10)),
1023				pays_fee: Pays::Yes
1024			}
1025		);
1026
1027		let mut post_dispatch = PostDispatchInfo { actual_weight: None, pays_fee: Pays::Yes };
1028		post_dispatch.refund(Weight::from_parts(100, 15));
1029		assert_eq!(post_dispatch, PostDispatchInfo { actual_weight: None, pays_fee: Pays::Yes });
1030	}
1031}
1032
1033#[cfg(test)]
1034mod per_dispatch_class_tests {
1035	use super::*;
1036	use pezsp_runtime::traits::Zero;
1037	use DispatchClass::*;
1038
1039	#[test]
1040	fn add_works() {
1041		let a = PerDispatchClass {
1042			normal: (5, 10).into(),
1043			operational: (20, 30).into(),
1044			mandatory: Weight::MAX,
1045		};
1046		assert_eq!(
1047			a.clone()
1048				.add((20, 5).into(), Normal)
1049				.add((10, 10).into(), Operational)
1050				.add((u64::MAX, 3).into(), Mandatory),
1051			PerDispatchClass {
1052				normal: (25, 15).into(),
1053				operational: (30, 40).into(),
1054				mandatory: Weight::MAX
1055			}
1056		);
1057		let b = a
1058			.add(Weight::MAX, Normal)
1059			.add(Weight::MAX, Operational)
1060			.add(Weight::MAX, Mandatory);
1061		assert_eq!(
1062			b,
1063			PerDispatchClass {
1064				normal: Weight::MAX,
1065				operational: Weight::MAX,
1066				mandatory: Weight::MAX
1067			}
1068		);
1069		assert_eq!(b.total(), Weight::MAX);
1070	}
1071
1072	#[test]
1073	fn accrue_works() {
1074		let mut a = PerDispatchClass::default();
1075
1076		a.accrue((10, 15).into(), Normal);
1077		assert_eq!(a.normal, (10, 15).into());
1078		assert_eq!(a.total(), (10, 15).into());
1079
1080		a.accrue((20, 25).into(), Operational);
1081		assert_eq!(a.operational, (20, 25).into());
1082		assert_eq!(a.total(), (30, 40).into());
1083
1084		a.accrue((30, 35).into(), Mandatory);
1085		assert_eq!(a.mandatory, (30, 35).into());
1086		assert_eq!(a.total(), (60, 75).into());
1087
1088		a.accrue((u64::MAX, 10).into(), Operational);
1089		assert_eq!(a.operational, (u64::MAX, 35).into());
1090		assert_eq!(a.total(), (u64::MAX, 85).into());
1091
1092		a.accrue((10, u64::MAX).into(), Normal);
1093		assert_eq!(a.normal, (20, u64::MAX).into());
1094		assert_eq!(a.total(), Weight::MAX);
1095	}
1096
1097	#[test]
1098	fn reduce_works() {
1099		let mut a = PerDispatchClass {
1100			normal: (10, u64::MAX).into(),
1101			mandatory: (u64::MAX, 10).into(),
1102			operational: (20, 20).into(),
1103		};
1104
1105		a.reduce((5, 100).into(), Normal);
1106		assert_eq!(a.normal, (5, u64::MAX - 100).into());
1107		assert_eq!(a.total(), (u64::MAX, u64::MAX - 70).into());
1108
1109		a.reduce((15, 5).into(), Operational);
1110		assert_eq!(a.operational, (5, 15).into());
1111		assert_eq!(a.total(), (u64::MAX, u64::MAX - 75).into());
1112
1113		a.reduce((50, 0).into(), Mandatory);
1114		assert_eq!(a.mandatory, (u64::MAX - 50, 10).into());
1115		assert_eq!(a.total(), (u64::MAX - 40, u64::MAX - 75).into());
1116
1117		a.reduce((u64::MAX, 100).into(), Operational);
1118		assert!(a.operational.is_zero());
1119		assert_eq!(a.total(), (u64::MAX - 45, u64::MAX - 90).into());
1120
1121		a.reduce((5, u64::MAX).into(), Normal);
1122		assert!(a.normal.is_zero());
1123		assert_eq!(a.total(), (u64::MAX - 50, 10).into());
1124	}
1125
1126	#[test]
1127	fn checked_accrue_works() {
1128		let mut a = PerDispatchClass::default();
1129
1130		a.checked_accrue((1, 2).into(), Normal).unwrap();
1131		a.checked_accrue((3, 4).into(), Operational).unwrap();
1132		a.checked_accrue((5, 6).into(), Mandatory).unwrap();
1133		a.checked_accrue((7, 8).into(), Operational).unwrap();
1134		a.checked_accrue((9, 0).into(), Normal).unwrap();
1135
1136		assert_eq!(
1137			a,
1138			PerDispatchClass {
1139				normal: (10, 2).into(),
1140				operational: (10, 12).into(),
1141				mandatory: (5, 6).into(),
1142			}
1143		);
1144
1145		a.checked_accrue((u64::MAX - 10, u64::MAX - 2).into(), Normal).unwrap();
1146		a.checked_accrue((0, 0).into(), Normal).unwrap();
1147		a.checked_accrue((1, 0).into(), Normal).unwrap_err();
1148		a.checked_accrue((0, 1).into(), Normal).unwrap_err();
1149
1150		assert_eq!(
1151			a,
1152			PerDispatchClass {
1153				normal: Weight::MAX,
1154				operational: (10, 12).into(),
1155				mandatory: (5, 6).into(),
1156			}
1157		);
1158	}
1159
1160	#[test]
1161	fn checked_accrue_does_not_modify_on_error() {
1162		let mut a = PerDispatchClass {
1163			normal: 0.into(),
1164			operational: Weight::MAX / 2 + 2.into(),
1165			mandatory: 10.into(),
1166		};
1167
1168		a.checked_accrue(Weight::MAX / 2, Operational).unwrap_err();
1169		a.checked_accrue(Weight::MAX - 9.into(), Mandatory).unwrap_err();
1170		a.checked_accrue(Weight::MAX, Normal).unwrap(); // This one works
1171
1172		assert_eq!(
1173			a,
1174			PerDispatchClass {
1175				normal: Weight::MAX,
1176				operational: Weight::MAX / 2 + 2.into(),
1177				mandatory: 10.into(),
1178			}
1179		);
1180	}
1181
1182	#[test]
1183	fn total_works() {
1184		assert!(PerDispatchClass::default().total().is_zero());
1185
1186		assert_eq!(
1187			PerDispatchClass {
1188				normal: 0.into(),
1189				operational: (10, 20).into(),
1190				mandatory: (20, u64::MAX).into(),
1191			}
1192			.total(),
1193			(30, u64::MAX).into()
1194		);
1195
1196		assert_eq!(
1197			PerDispatchClass {
1198				normal: (u64::MAX - 10, 10).into(),
1199				operational: (3, u64::MAX).into(),
1200				mandatory: (4, u64::MAX).into(),
1201			}
1202			.total(),
1203			(u64::MAX - 3, u64::MAX).into()
1204		);
1205	}
1206}
1207
1208#[cfg(test)]
1209mod test_extensions {
1210	use codec::{Decode, DecodeWithMemTracking, Encode};
1211	use pezsp_runtime::{
1212		impl_tx_ext_default,
1213		traits::{
1214			DispatchInfoOf, DispatchOriginOf, Dispatchable, PostDispatchInfoOf,
1215			TransactionExtension,
1216		},
1217		transaction_validity::TransactionValidityError,
1218	};
1219	use pezsp_weights::Weight;
1220	use scale_info::TypeInfo;
1221
1222	use super::{DispatchResult, PostDispatchInfo};
1223
1224	/// Test extension that refunds half its cost if the preset inner flag is set.
1225	#[derive(Clone, Eq, PartialEq, Debug, Encode, Decode, DecodeWithMemTracking, TypeInfo)]
1226	pub struct HalfCostIf(pub bool);
1227
1228	impl<RuntimeCall: Dispatchable> TransactionExtension<RuntimeCall> for HalfCostIf {
1229		const IDENTIFIER: &'static str = "HalfCostIf";
1230		type Implicit = ();
1231		type Val = ();
1232		type Pre = bool;
1233
1234		fn weight(&self, _: &RuntimeCall) -> pezsp_weights::Weight {
1235			Weight::from_parts(100, 0)
1236		}
1237
1238		fn prepare(
1239			self,
1240			_val: Self::Val,
1241			_origin: &DispatchOriginOf<RuntimeCall>,
1242			_call: &RuntimeCall,
1243			_info: &DispatchInfoOf<RuntimeCall>,
1244			_len: usize,
1245		) -> Result<Self::Pre, TransactionValidityError> {
1246			Ok(self.0)
1247		}
1248
1249		fn post_dispatch_details(
1250			pre: Self::Pre,
1251			_info: &DispatchInfoOf<RuntimeCall>,
1252			_post_info: &PostDispatchInfoOf<RuntimeCall>,
1253			_len: usize,
1254			_result: &DispatchResult,
1255		) -> Result<Weight, TransactionValidityError> {
1256			if pre {
1257				Ok(Weight::from_parts(50, 0))
1258			} else {
1259				Ok(Weight::zero())
1260			}
1261		}
1262		impl_tx_ext_default!(RuntimeCall; validate);
1263	}
1264
1265	/// Test extension that refunds its cost if the actual post dispatch weight up until this point
1266	/// in the extension pipeline is less than the preset inner `ref_time` amount.
1267	#[derive(Clone, Eq, PartialEq, Debug, Encode, Decode, DecodeWithMemTracking, TypeInfo)]
1268	pub struct FreeIfUnder(pub u64);
1269
1270	impl<RuntimeCall: Dispatchable> TransactionExtension<RuntimeCall> for FreeIfUnder
1271	where
1272		RuntimeCall: Dispatchable<PostInfo = PostDispatchInfo>,
1273	{
1274		const IDENTIFIER: &'static str = "FreeIfUnder";
1275		type Implicit = ();
1276		type Val = ();
1277		type Pre = u64;
1278
1279		fn weight(&self, _: &RuntimeCall) -> pezsp_weights::Weight {
1280			Weight::from_parts(200, 0)
1281		}
1282
1283		fn prepare(
1284			self,
1285			_val: Self::Val,
1286			_origin: &DispatchOriginOf<RuntimeCall>,
1287			_call: &RuntimeCall,
1288			_info: &DispatchInfoOf<RuntimeCall>,
1289			_len: usize,
1290		) -> Result<Self::Pre, TransactionValidityError> {
1291			Ok(self.0)
1292		}
1293
1294		fn post_dispatch_details(
1295			pre: Self::Pre,
1296			_info: &DispatchInfoOf<RuntimeCall>,
1297			post_info: &PostDispatchInfoOf<RuntimeCall>,
1298			_len: usize,
1299			_result: &DispatchResult,
1300		) -> Result<Weight, TransactionValidityError> {
1301			if let Some(actual) = post_info.actual_weight {
1302				if pre > actual.ref_time() {
1303					return Ok(Weight::from_parts(200, 0));
1304				}
1305			}
1306			Ok(Weight::zero())
1307		}
1308		impl_tx_ext_default!(RuntimeCall; validate);
1309	}
1310
1311	/// Test extension that sets its actual post dispatch `ref_time` weight to the preset inner
1312	/// amount.
1313	#[derive(Clone, Eq, PartialEq, Debug, Encode, Decode, DecodeWithMemTracking, TypeInfo)]
1314	pub struct ActualWeightIs(pub u64);
1315
1316	impl<RuntimeCall: Dispatchable> TransactionExtension<RuntimeCall> for ActualWeightIs {
1317		const IDENTIFIER: &'static str = "ActualWeightIs";
1318		type Implicit = ();
1319		type Val = ();
1320		type Pre = u64;
1321
1322		fn weight(&self, _: &RuntimeCall) -> pezsp_weights::Weight {
1323			Weight::from_parts(300, 0)
1324		}
1325
1326		fn prepare(
1327			self,
1328			_val: Self::Val,
1329			_origin: &DispatchOriginOf<RuntimeCall>,
1330			_call: &RuntimeCall,
1331			_info: &DispatchInfoOf<RuntimeCall>,
1332			_len: usize,
1333		) -> Result<Self::Pre, TransactionValidityError> {
1334			Ok(self.0)
1335		}
1336
1337		fn post_dispatch_details(
1338			pre: Self::Pre,
1339			_info: &DispatchInfoOf<RuntimeCall>,
1340			_post_info: &PostDispatchInfoOf<RuntimeCall>,
1341			_len: usize,
1342			_result: &DispatchResult,
1343		) -> Result<Weight, TransactionValidityError> {
1344			Ok(Weight::from_parts(300u64.saturating_sub(pre), 0))
1345		}
1346		impl_tx_ext_default!(RuntimeCall; validate);
1347	}
1348}
1349
1350#[cfg(test)]
1351// Do not complain about unused `dispatch` and `dispatch_aux`.
1352#[allow(dead_code)]
1353mod extension_weight_tests {
1354	use crate::assert_ok;
1355
1356	use super::*;
1357	use pezsp_core::parameter_types;
1358	use pezsp_runtime::{
1359		generic::{self, ExtrinsicFormat},
1360		traits::{Applyable, BlakeTwo256, DispatchTransaction, TransactionExtension},
1361	};
1362	use pezsp_weights::RuntimeDbWeight;
1363	use test_extensions::{ActualWeightIs, FreeIfUnder, HalfCostIf};
1364
1365	use super::weight_tests::pezframe_system;
1366	use pezframe_support::construct_runtime;
1367
1368	pub type TxExtension = (HalfCostIf, FreeIfUnder, ActualWeightIs);
1369	pub type UncheckedExtrinsic = generic::UncheckedExtrinsic<u64, RuntimeCall, (), TxExtension>;
1370	pub type Header = generic::Header<BlockNumber, BlakeTwo256>;
1371	pub type Block = generic::Block<Header, UncheckedExtrinsic>;
1372	pub type AccountId = u64;
1373	pub type Balance = u32;
1374	pub type BlockNumber = u32;
1375
1376	construct_runtime!(
1377		pub enum ExtRuntime {
1378			System: pezframe_system,
1379		}
1380	);
1381
1382	impl pezframe_system::Config for ExtRuntime {
1383		type Block = Block;
1384		type AccountId = AccountId;
1385		type Balance = Balance;
1386		type BaseCallFilter = crate::traits::Everything;
1387		type RuntimeOrigin = RuntimeOrigin;
1388		type RuntimeCall = RuntimeCall;
1389		type RuntimeTask = RuntimeTask;
1390		type DbWeight = DbWeight;
1391		type PalletInfo = PalletInfo;
1392	}
1393
1394	parameter_types! {
1395		pub const DbWeight: RuntimeDbWeight = RuntimeDbWeight {
1396			read: 100,
1397			write: 1000,
1398		};
1399	}
1400
1401	pub struct ExtBuilder {}
1402
1403	impl Default for ExtBuilder {
1404		fn default() -> Self {
1405			Self {}
1406		}
1407	}
1408
1409	impl ExtBuilder {
1410		pub fn build(self) -> pezsp_io::TestExternalities {
1411			let mut ext = pezsp_io::TestExternalities::new(Default::default());
1412			ext.execute_with(|| {});
1413			ext
1414		}
1415
1416		pub fn build_and_execute(self, test: impl FnOnce() -> ()) {
1417			self.build().execute_with(|| {
1418				test();
1419			})
1420		}
1421	}
1422
1423	#[test]
1424	fn no_post_dispatch_with_no_refund() {
1425		ExtBuilder::default().build_and_execute(|| {
1426			let call = RuntimeCall::System(pezframe_system::Call::<ExtRuntime>::f99 {});
1427			let ext: TxExtension = (HalfCostIf(false), FreeIfUnder(1500), ActualWeightIs(0));
1428			let uxt = UncheckedExtrinsic::new_signed(call.clone(), 0, (), ext.clone());
1429			assert_eq!(uxt.extension_weight(), Weight::from_parts(600, 0));
1430
1431			let mut info = call.get_dispatch_info();
1432			assert_eq!(info.total_weight(), Weight::from_parts(1000, 0));
1433			info.extension_weight = ext.weight(&call);
1434			let (pre, _) = ext.validate_and_prepare(Some(0).into(), &call, &info, 0, 0).unwrap();
1435			let res = call.dispatch(Some(0).into());
1436			let mut post_info = res.unwrap();
1437			assert!(post_info.actual_weight.is_none());
1438			assert_ok!(<TxExtension as TransactionExtension<RuntimeCall>>::post_dispatch(
1439				pre,
1440				&info,
1441				&mut post_info,
1442				0,
1443				&Ok(()),
1444			));
1445			assert!(post_info.actual_weight.is_none());
1446		});
1447	}
1448
1449	#[test]
1450	fn no_post_dispatch_refunds_when_dispatched() {
1451		ExtBuilder::default().build_and_execute(|| {
1452			let call = RuntimeCall::System(pezframe_system::Call::<ExtRuntime>::f99 {});
1453			let ext: TxExtension = (HalfCostIf(true), FreeIfUnder(100), ActualWeightIs(0));
1454			let uxt = UncheckedExtrinsic::new_signed(call.clone(), 0, (), ext.clone());
1455			assert_eq!(uxt.extension_weight(), Weight::from_parts(600, 0));
1456
1457			let mut info = call.get_dispatch_info();
1458			assert_eq!(info.total_weight(), Weight::from_parts(1000, 0));
1459			info.extension_weight = ext.weight(&call);
1460			let post_info =
1461				ext.dispatch_transaction(Some(0).into(), call, &info, 0, 0).unwrap().unwrap();
1462			// 1000 call weight + 50 + 200 + 0
1463			assert_eq!(post_info.actual_weight, Some(Weight::from_parts(1250, 0)));
1464		});
1465	}
1466
1467	#[test]
1468	fn post_dispatch_with_refunds() {
1469		ExtBuilder::default().build_and_execute(|| {
1470			let call = RuntimeCall::System(pezframe_system::Call::<ExtRuntime>::f100 {});
1471			// First testcase
1472			let ext: TxExtension = (HalfCostIf(false), FreeIfUnder(2000), ActualWeightIs(0));
1473			let uxt = UncheckedExtrinsic::new_signed(call.clone(), 0, (), ext.clone());
1474			assert_eq!(uxt.extension_weight(), Weight::from_parts(600, 0));
1475
1476			let mut info = call.get_dispatch_info();
1477			assert_eq!(info.call_weight, Weight::from_parts(1000, 0));
1478			info.extension_weight = ext.weight(&call);
1479			assert_eq!(info.total_weight(), Weight::from_parts(1600, 0));
1480			let (pre, _) = ext.validate_and_prepare(Some(0).into(), &call, &info, 0, 0).unwrap();
1481			let res = call.clone().dispatch(Some(0).into());
1482			let mut post_info = res.unwrap();
1483			// 500 actual call weight
1484			assert_eq!(post_info.actual_weight, Some(Weight::from_parts(500, 0)));
1485			// add the 600 worst case extension weight
1486			post_info.set_extension_weight(&info);
1487			// extension weight should be refunded
1488			assert_ok!(<TxExtension as TransactionExtension<RuntimeCall>>::post_dispatch(
1489				pre,
1490				&info,
1491				&mut post_info,
1492				0,
1493				&Ok(()),
1494			));
1495			// 500 actual call weight + 100 + 0 + 0
1496			assert_eq!(post_info.actual_weight, Some(Weight::from_parts(600, 0)));
1497
1498			// Second testcase
1499			let ext: TxExtension = (HalfCostIf(false), FreeIfUnder(1100), ActualWeightIs(200));
1500			let (pre, _) = ext.validate_and_prepare(Some(0).into(), &call, &info, 0, 0).unwrap();
1501			let res = call.clone().dispatch(Some(0).into());
1502			let mut post_info = res.unwrap();
1503			// 500 actual call weight
1504			assert_eq!(post_info.actual_weight, Some(Weight::from_parts(500, 0)));
1505			// add the 600 worst case extension weight
1506			post_info.set_extension_weight(&info);
1507			// extension weight should be refunded
1508			assert_ok!(<TxExtension as TransactionExtension<RuntimeCall>>::post_dispatch(
1509				pre,
1510				&info,
1511				&mut post_info,
1512				0,
1513				&Ok(()),
1514			));
1515			// 500 actual call weight + 100 + 200 + 200
1516			assert_eq!(post_info.actual_weight, Some(Weight::from_parts(1000, 0)));
1517
1518			// Third testcase
1519			let ext: TxExtension = (HalfCostIf(true), FreeIfUnder(1060), ActualWeightIs(200));
1520			let (pre, _) = ext.validate_and_prepare(Some(0).into(), &call, &info, 0, 0).unwrap();
1521			let res = call.clone().dispatch(Some(0).into());
1522			let mut post_info = res.unwrap();
1523			// 500 actual call weight
1524			assert_eq!(post_info.actual_weight, Some(Weight::from_parts(500, 0)));
1525			// add the 600 worst case extension weight
1526			post_info.set_extension_weight(&info);
1527			// extension weight should be refunded
1528			assert_ok!(<TxExtension as TransactionExtension<RuntimeCall>>::post_dispatch(
1529				pre,
1530				&info,
1531				&mut post_info,
1532				0,
1533				&Ok(()),
1534			));
1535			// 500 actual call weight + 50 + 0 + 200
1536			assert_eq!(post_info.actual_weight, Some(Weight::from_parts(750, 0)));
1537
1538			// Fourth testcase
1539			let ext: TxExtension = (HalfCostIf(false), FreeIfUnder(100), ActualWeightIs(300));
1540			let (pre, _) = ext.validate_and_prepare(Some(0).into(), &call, &info, 0, 0).unwrap();
1541			let res = call.clone().dispatch(Some(0).into());
1542			let mut post_info = res.unwrap();
1543			// 500 actual call weight
1544			assert_eq!(post_info.actual_weight, Some(Weight::from_parts(500, 0)));
1545			// add the 600 worst case extension weight
1546			post_info.set_extension_weight(&info);
1547			// extension weight should be refunded
1548			assert_ok!(<TxExtension as TransactionExtension<RuntimeCall>>::post_dispatch(
1549				pre,
1550				&info,
1551				&mut post_info,
1552				0,
1553				&Ok(()),
1554			));
1555			// 500 actual call weight + 100 + 200 + 300
1556			assert_eq!(post_info.actual_weight, Some(Weight::from_parts(1100, 0)));
1557		});
1558	}
1559
1560	#[test]
1561	fn checked_extrinsic_apply() {
1562		ExtBuilder::default().build_and_execute(|| {
1563			let call = RuntimeCall::System(pezframe_system::Call::<ExtRuntime>::f100 {});
1564			// First testcase
1565			let ext: TxExtension = (HalfCostIf(false), FreeIfUnder(2000), ActualWeightIs(0));
1566			let xt = CheckedExtrinsic {
1567				format: ExtrinsicFormat::Signed(0, ext.clone()),
1568				function: call.clone(),
1569			};
1570			assert_eq!(xt.extension_weight(), Weight::from_parts(600, 0));
1571			let mut info = call.get_dispatch_info();
1572			assert_eq!(info.call_weight, Weight::from_parts(1000, 0));
1573			info.extension_weight = ext.weight(&call);
1574			assert_eq!(info.total_weight(), Weight::from_parts(1600, 0));
1575			let post_info = xt.apply::<ExtRuntime>(&info, 0).unwrap().unwrap();
1576			// 500 actual call weight + 100 + 0 + 0
1577			assert_eq!(post_info.actual_weight, Some(Weight::from_parts(600, 0)));
1578
1579			// Second testcase
1580			let ext: TxExtension = (HalfCostIf(false), FreeIfUnder(1100), ActualWeightIs(200));
1581			let xt = CheckedExtrinsic {
1582				format: ExtrinsicFormat::Signed(0, ext),
1583				function: call.clone(),
1584			};
1585			let post_info = xt.apply::<ExtRuntime>(&info, 0).unwrap().unwrap();
1586			// 500 actual call weight + 100 + 200 + 200
1587			assert_eq!(post_info.actual_weight, Some(Weight::from_parts(1000, 0)));
1588
1589			// Third testcase
1590			let ext: TxExtension = (HalfCostIf(true), FreeIfUnder(1060), ActualWeightIs(200));
1591			let xt = CheckedExtrinsic {
1592				format: ExtrinsicFormat::Signed(0, ext),
1593				function: call.clone(),
1594			};
1595			let post_info = xt.apply::<ExtRuntime>(&info, 0).unwrap().unwrap();
1596			// 500 actual call weight + 50 + 0 + 200
1597			assert_eq!(post_info.actual_weight, Some(Weight::from_parts(750, 0)));
1598
1599			// Fourth testcase
1600			let ext: TxExtension = (HalfCostIf(false), FreeIfUnder(100), ActualWeightIs(300));
1601			let xt = CheckedExtrinsic {
1602				format: ExtrinsicFormat::Signed(0, ext),
1603				function: call.clone(),
1604			};
1605			let post_info = xt.apply::<ExtRuntime>(&info, 0).unwrap().unwrap();
1606			// 500 actual call weight + 100 + 200 + 300
1607			assert_eq!(post_info.actual_weight, Some(Weight::from_parts(1100, 0)));
1608		});
1609	}
1610}