Skip to main content

topsoil_core/
dispatch.rs

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