1use crate::traits::UnfilteredDispatchable;
22use codec::{Codec, Decode, DecodeWithMemTracking, Encode, EncodeLike, MaxEncodedLen};
23use core::fmt;
24use scale_info::TypeInfo;
25#[cfg(feature = "std")]
26use serde::{Deserialize, Serialize};
27use sp_runtime::{
28 generic::{CheckedExtrinsic, UncheckedExtrinsic},
29 traits::{
30 Dispatchable, ExtensionPostDispatchWeightHandler, RefundWeight, TransactionExtension,
31 },
32 DispatchError, RuntimeDebug,
33};
34use sp_weights::Weight;
35
36pub type DispatchResultWithPostInfo = sp_runtime::DispatchResultWithInfo<PostDispatchInfo>;
40
41#[docify::export]
42pub type DispatchResult = Result<(), sp_runtime::DispatchError>;
47
48pub type DispatchErrorWithPostInfo = sp_runtime::DispatchErrorWithPostInfo<PostDispatchInfo>;
50
51pub trait Callable<T> {
53 type RuntimeCall: UnfilteredDispatchable + Codec + Clone + PartialEq + Eq;
54}
55
56pub type CallableCallFor<A, R> = <A as Callable<R>>::RuntimeCall;
59
60pub trait CheckIfFeeless {
66 type Origin;
68
69 fn is_feeless(&self, origin: &Self::Origin) -> bool;
72}
73
74#[derive(
76 PartialEq,
77 Eq,
78 Clone,
79 RuntimeDebug,
80 Encode,
81 Decode,
82 DecodeWithMemTracking,
83 TypeInfo,
84 MaxEncodedLen,
85)]
86pub enum RawOrigin<AccountId> {
87 Root,
89 Signed(AccountId),
91 None,
95}
96
97impl<AccountId> From<Option<AccountId>> for RawOrigin<AccountId> {
98 fn from(s: Option<AccountId>) -> RawOrigin<AccountId> {
99 match s {
100 Some(who) => RawOrigin::Signed(who),
101 None => RawOrigin::None,
102 }
103 }
104}
105
106impl<AccountId> RawOrigin<AccountId> {
107 pub fn as_signed(&self) -> Option<&AccountId> {
109 match &self {
110 Self::Signed(x) => Some(x),
111 _ => None,
112 }
113 }
114
115 pub fn is_root(&self) -> bool {
117 matches!(&self, Self::Root)
118 }
119
120 pub fn is_none(&self) -> bool {
122 matches!(&self, Self::None)
123 }
124}
125
126pub trait Parameter:
130 Codec + DecodeWithMemTracking + EncodeLike + Clone + Eq + fmt::Debug + scale_info::TypeInfo
131{
132}
133impl<T> Parameter for T where
134 T: Codec + DecodeWithMemTracking + EncodeLike + Clone + Eq + fmt::Debug + scale_info::TypeInfo
135{
136}
137
138pub trait ClassifyDispatch<T> {
140 fn classify_dispatch(&self, target: T) -> DispatchClass;
144}
145
146pub trait PaysFee<T> {
150 fn pays_fee(&self, _target: T) -> Pays;
151}
152
153#[derive(
155 Clone, Copy, Eq, PartialEq, RuntimeDebug, Encode, Decode, DecodeWithMemTracking, TypeInfo,
156)]
157pub enum Pays {
158 Yes,
160 No,
162}
163
164impl Default for Pays {
165 fn default() -> Self {
166 Self::Yes
167 }
168}
169
170impl From<Pays> for PostDispatchInfo {
171 fn from(pays_fee: Pays) -> Self {
172 Self { actual_weight: None, pays_fee }
173 }
174}
175
176impl From<bool> for Pays {
177 fn from(b: bool) -> Self {
178 match b {
179 true => Self::Yes,
180 false => Self::No,
181 }
182 }
183}
184
185#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
190#[cfg_attr(feature = "std", serde(rename_all = "camelCase"))]
191#[derive(
192 PartialEq, Eq, Clone, Copy, Encode, Decode, DecodeWithMemTracking, RuntimeDebug, TypeInfo,
193)]
194pub enum DispatchClass {
195 Normal,
197 Operational,
199 Mandatory,
213}
214
215impl Default for DispatchClass {
216 fn default() -> Self {
217 Self::Normal
218 }
219}
220
221impl DispatchClass {
222 pub fn all() -> &'static [DispatchClass] {
224 &[DispatchClass::Normal, DispatchClass::Operational, DispatchClass::Mandatory]
225 }
226
227 pub fn non_mandatory() -> &'static [DispatchClass] {
229 &[DispatchClass::Normal, DispatchClass::Operational]
230 }
231}
232
233pub trait OneOrMany<T> {
238 type Iter: Iterator<Item = T>;
240 fn into_iter(self) -> Self::Iter;
242}
243
244impl OneOrMany<DispatchClass> for DispatchClass {
245 type Iter = core::iter::Once<DispatchClass>;
246 fn into_iter(self) -> Self::Iter {
247 core::iter::once(self)
248 }
249}
250
251impl<'a> OneOrMany<DispatchClass> for &'a [DispatchClass] {
252 type Iter = core::iter::Cloned<core::slice::Iter<'a, DispatchClass>>;
253 fn into_iter(self) -> Self::Iter {
254 self.iter().cloned()
255 }
256}
257
258#[derive(Clone, Copy, Eq, PartialEq, Default, RuntimeDebug, Encode, Decode, TypeInfo)]
260pub struct DispatchInfo {
261 pub call_weight: Weight,
263 pub extension_weight: Weight,
265 pub class: DispatchClass,
267 pub pays_fee: Pays,
269}
270
271impl DispatchInfo {
272 pub fn total_weight(&self) -> Weight {
274 self.call_weight.saturating_add(self.extension_weight)
275 }
276}
277
278pub trait GetDispatchInfo {
281 fn get_dispatch_info(&self) -> DispatchInfo;
285}
286
287impl GetDispatchInfo for () {
288 fn get_dispatch_info(&self) -> DispatchInfo {
289 DispatchInfo::default()
290 }
291}
292
293pub fn extract_actual_weight(result: &DispatchResultWithPostInfo, info: &DispatchInfo) -> Weight {
295 match result {
296 Ok(post_info) => post_info,
297 Err(err) => &err.post_info,
298 }
299 .calc_actual_weight(info)
300}
301
302pub fn extract_actual_pays_fee(result: &DispatchResultWithPostInfo, info: &DispatchInfo) -> Pays {
305 match result {
306 Ok(post_info) => post_info,
307 Err(err) => &err.post_info,
308 }
309 .pays_fee(info)
310}
311
312#[derive(
315 Clone,
316 Copy,
317 Eq,
318 PartialEq,
319 Default,
320 RuntimeDebug,
321 Encode,
322 Decode,
323 DecodeWithMemTracking,
324 TypeInfo,
325)]
326pub struct PostDispatchInfo {
327 pub actual_weight: Option<Weight>,
329 pub pays_fee: Pays,
331}
332
333impl PostDispatchInfo {
334 pub fn calc_unspent(&self, info: &DispatchInfo) -> Weight {
336 info.total_weight() - self.calc_actual_weight(info)
337 }
338
339 pub fn calc_actual_weight(&self, info: &DispatchInfo) -> Weight {
341 if let Some(actual_weight) = self.actual_weight {
342 let info_total_weight = info.total_weight();
343 if actual_weight.any_gt(info_total_weight) {
344 log::error!(
345 target: crate::LOG_TARGET,
346 "Post dispatch weight is greater than pre dispatch weight. \
347 Pre dispatch weight may underestimating the actual weight. \
348 Greater post dispatch weight components are ignored.
349 Pre dispatch weight: {info_total_weight:?},
350 Post dispatch weight: {actual_weight:?}",
351 );
352 }
353 actual_weight.min(info.total_weight())
354 } else {
355 info.total_weight()
356 }
357 }
358
359 pub fn pays_fee(&self, info: &DispatchInfo) -> Pays {
361 if info.pays_fee == Pays::No || self.pays_fee == Pays::No {
366 Pays::No
367 } else {
368 Pays::Yes
370 }
371 }
372}
373
374impl From<()> for PostDispatchInfo {
375 fn from(_: ()) -> Self {
376 Self { actual_weight: None, pays_fee: Default::default() }
377 }
378}
379
380impl sp_runtime::traits::Printable for PostDispatchInfo {
381 fn print(&self) {
382 "actual_weight=".print();
383 match self.actual_weight {
384 Some(weight) => weight.print(),
385 None => "max-weight".print(),
386 };
387 "pays_fee=".print();
388 match self.pays_fee {
389 Pays::Yes => "Yes".print(),
390 Pays::No => "No".print(),
391 }
392 }
393}
394
395pub trait WithPostDispatchInfo {
398 fn with_weight(self, actual_weight: Weight) -> DispatchErrorWithPostInfo;
407}
408
409impl<T> WithPostDispatchInfo for T
410where
411 T: Into<DispatchError>,
412{
413 fn with_weight(self, actual_weight: Weight) -> DispatchErrorWithPostInfo {
414 DispatchErrorWithPostInfo {
415 post_info: PostDispatchInfo {
416 actual_weight: Some(actual_weight),
417 pays_fee: Default::default(),
418 },
419 error: self.into(),
420 }
421 }
422}
423
424impl<Address, Call: Dispatchable, Signature, Extension: TransactionExtension<Call>> GetDispatchInfo
426 for UncheckedExtrinsic<Address, Call, Signature, Extension>
427where
428 Call: GetDispatchInfo + Dispatchable,
429{
430 fn get_dispatch_info(&self) -> DispatchInfo {
431 let mut info = self.function.get_dispatch_info();
432 info.extension_weight = self.extension_weight();
433 info
434 }
435}
436
437impl<AccountId, Call: Dispatchable, Extension: TransactionExtension<Call>> GetDispatchInfo
439 for CheckedExtrinsic<AccountId, Call, Extension>
440where
441 Call: GetDispatchInfo,
442{
443 fn get_dispatch_info(&self) -> DispatchInfo {
444 let mut info = self.function.get_dispatch_info();
445 info.extension_weight = self.extension_weight();
446 info
447 }
448}
449
450#[derive(Clone, Eq, PartialEq, Default, Debug, Encode, Decode, TypeInfo, MaxEncodedLen)]
452pub struct PerDispatchClass<T> {
453 normal: T,
455 operational: T,
457 mandatory: T,
459}
460
461impl<T> PerDispatchClass<T> {
462 pub fn new(val: impl Fn(DispatchClass) -> T) -> Self {
464 Self {
465 normal: val(DispatchClass::Normal),
466 operational: val(DispatchClass::Operational),
467 mandatory: val(DispatchClass::Mandatory),
468 }
469 }
470
471 pub fn get_mut(&mut self, class: DispatchClass) -> &mut T {
473 match class {
474 DispatchClass::Operational => &mut self.operational,
475 DispatchClass::Normal => &mut self.normal,
476 DispatchClass::Mandatory => &mut self.mandatory,
477 }
478 }
479
480 pub fn get(&self, class: DispatchClass) -> &T {
482 match class {
483 DispatchClass::Normal => &self.normal,
484 DispatchClass::Operational => &self.operational,
485 DispatchClass::Mandatory => &self.mandatory,
486 }
487 }
488}
489
490impl<T: Clone> PerDispatchClass<T> {
491 pub fn set(&mut self, new: T, class: impl OneOrMany<DispatchClass>) {
493 for class in class.into_iter() {
494 *self.get_mut(class) = new.clone();
495 }
496 }
497}
498
499impl PerDispatchClass<Weight> {
500 pub fn total(&self) -> Weight {
504 let mut sum = Weight::zero();
505 for class in DispatchClass::all() {
506 sum.saturating_accrue(*self.get(*class));
507 }
508 sum
509 }
510
511 pub fn add(mut self, weight: Weight, class: DispatchClass) -> Self {
513 self.accrue(weight, class);
514 self
515 }
516
517 pub fn accrue(&mut self, weight: Weight, class: DispatchClass) {
519 self.get_mut(class).saturating_accrue(weight);
520 }
521
522 pub fn checked_accrue(&mut self, weight: Weight, class: DispatchClass) -> Result<(), ()> {
524 self.get_mut(class).checked_accrue(weight).ok_or(())
525 }
526
527 pub fn reduce(&mut self, weight: Weight, class: DispatchClass) {
529 self.get_mut(class).saturating_reduce(weight);
530 }
531}
532
533pub trait WeighData<T> {
535 fn weigh_data(&self, target: T) -> Weight;
538}
539
540impl<T> WeighData<T> for Weight {
541 fn weigh_data(&self, _: T) -> Weight {
542 return *self
543 }
544}
545
546impl<T> PaysFee<T> for (Weight, DispatchClass, Pays) {
547 fn pays_fee(&self, _: T) -> Pays {
548 self.2
549 }
550}
551
552impl<T> WeighData<T> for (Weight, DispatchClass) {
553 fn weigh_data(&self, args: T) -> Weight {
554 return self.0.weigh_data(args)
555 }
556}
557
558impl<T> WeighData<T> for (Weight, DispatchClass, Pays) {
559 fn weigh_data(&self, args: T) -> Weight {
560 return self.0.weigh_data(args)
561 }
562}
563
564impl<T> ClassifyDispatch<T> for (Weight, DispatchClass) {
565 fn classify_dispatch(&self, _: T) -> DispatchClass {
566 self.1
567 }
568}
569
570impl<T> PaysFee<T> for (Weight, DispatchClass) {
571 fn pays_fee(&self, _: T) -> Pays {
572 Pays::Yes
573 }
574}
575
576impl<T> WeighData<T> for (Weight, Pays) {
577 fn weigh_data(&self, args: T) -> Weight {
578 return self.0.weigh_data(args)
579 }
580}
581
582impl<T> ClassifyDispatch<T> for (Weight, Pays) {
583 fn classify_dispatch(&self, _: T) -> DispatchClass {
584 DispatchClass::Normal
585 }
586}
587
588impl<T> PaysFee<T> for (Weight, Pays) {
589 fn pays_fee(&self, _: T) -> Pays {
590 self.1
591 }
592}
593
594impl From<(Option<Weight>, Pays)> for PostDispatchInfo {
595 fn from(post_weight_info: (Option<Weight>, Pays)) -> Self {
596 let (actual_weight, pays_fee) = post_weight_info;
597 Self { actual_weight, pays_fee }
598 }
599}
600
601impl From<Option<Weight>> for PostDispatchInfo {
602 fn from(actual_weight: Option<Weight>) -> Self {
603 Self { actual_weight, pays_fee: Default::default() }
604 }
605}
606
607impl<T> ClassifyDispatch<T> for Weight {
608 fn classify_dispatch(&self, _: T) -> DispatchClass {
609 DispatchClass::Normal
610 }
611}
612
613impl<T> PaysFee<T> for Weight {
614 fn pays_fee(&self, _: T) -> Pays {
615 Pays::Yes
616 }
617}
618
619impl<T> ClassifyDispatch<T> for (Weight, DispatchClass, Pays) {
620 fn classify_dispatch(&self, _: T) -> DispatchClass {
621 self.1
622 }
623}
624
625impl RefundWeight for PostDispatchInfo {
626 fn refund(&mut self, weight: Weight) {
627 if let Some(actual_weight) = self.actual_weight.as_mut() {
628 actual_weight.saturating_reduce(weight);
629 }
630 }
631}
632
633impl ExtensionPostDispatchWeightHandler<DispatchInfo> for PostDispatchInfo {
634 fn set_extension_weight(&mut self, info: &DispatchInfo) {
635 let actual_weight = self
636 .actual_weight
637 .unwrap_or(info.call_weight)
638 .saturating_add(info.extension_weight);
639 self.actual_weight = Some(actual_weight);
640 }
641}
642
643impl ExtensionPostDispatchWeightHandler<()> for PostDispatchInfo {
644 fn set_extension_weight(&mut self, _: &()) {}
645}
646
647impl<T> ClassifyDispatch<T> for u64 {
650 fn classify_dispatch(&self, _: T) -> DispatchClass {
651 DispatchClass::Normal
652 }
653}
654
655impl<T> PaysFee<T> for u64 {
656 fn pays_fee(&self, _: T) -> Pays {
657 Pays::Yes
658 }
659}
660
661impl<T> WeighData<T> for u64 {
662 fn weigh_data(&self, _: T) -> Weight {
663 return Weight::from_parts(*self, 0)
664 }
665}
666
667impl<T> WeighData<T> for (u64, DispatchClass, Pays) {
668 fn weigh_data(&self, args: T) -> Weight {
669 return self.0.weigh_data(args)
670 }
671}
672
673impl<T> ClassifyDispatch<T> for (u64, DispatchClass, Pays) {
674 fn classify_dispatch(&self, _: T) -> DispatchClass {
675 self.1
676 }
677}
678
679impl<T> PaysFee<T> for (u64, DispatchClass, Pays) {
680 fn pays_fee(&self, _: T) -> Pays {
681 self.2
682 }
683}
684
685impl<T> WeighData<T> for (u64, DispatchClass) {
686 fn weigh_data(&self, args: T) -> Weight {
687 return self.0.weigh_data(args)
688 }
689}
690
691impl<T> ClassifyDispatch<T> for (u64, DispatchClass) {
692 fn classify_dispatch(&self, _: T) -> DispatchClass {
693 self.1
694 }
695}
696
697impl<T> PaysFee<T> for (u64, DispatchClass) {
698 fn pays_fee(&self, _: T) -> Pays {
699 Pays::Yes
700 }
701}
702
703impl<T> WeighData<T> for (u64, Pays) {
704 fn weigh_data(&self, args: T) -> Weight {
705 return self.0.weigh_data(args)
706 }
707}
708
709impl<T> ClassifyDispatch<T> for (u64, Pays) {
710 fn classify_dispatch(&self, _: T) -> DispatchClass {
711 DispatchClass::Normal
712 }
713}
714
715impl<T> PaysFee<T> for (u64, Pays) {
716 fn pays_fee(&self, _: T) -> Pays {
717 self.1
718 }
719}
720
721#[cfg(test)]
724#[allow(dead_code)]
726mod weight_tests {
727 use super::*;
728 use sp_core::parameter_types;
729 use sp_runtime::{generic, traits::BlakeTwo256};
730 use sp_weights::RuntimeDbWeight;
731
732 pub use self::frame_system::{Call, Config};
733
734 fn from_actual_ref_time(ref_time: Option<u64>) -> PostDispatchInfo {
735 PostDispatchInfo {
736 actual_weight: ref_time.map(|t| Weight::from_all(t)),
737 pays_fee: Default::default(),
738 }
739 }
740
741 fn from_post_weight_info(ref_time: Option<u64>, pays_fee: Pays) -> PostDispatchInfo {
742 PostDispatchInfo { actual_weight: ref_time.map(|t| Weight::from_all(t)), pays_fee }
743 }
744
745 #[crate::pallet(dev_mode)]
746 pub mod frame_system {
747 use super::{frame_system, frame_system::pallet_prelude::*};
748 pub use crate::dispatch::RawOrigin;
749 use crate::pallet_prelude::*;
750
751 #[pallet::pallet]
752 pub struct Pallet<T>(_);
753
754 #[pallet::config]
755 #[pallet::disable_frame_system_supertrait_check]
756 pub trait Config: 'static {
757 type Block: Parameter + sp_runtime::traits::Block;
758 type AccountId;
759 type Balance;
760 type BaseCallFilter: crate::traits::Contains<Self::RuntimeCall>;
761 type RuntimeOrigin;
762 type RuntimeCall;
763 type RuntimeTask;
764 type PalletInfo: crate::traits::PalletInfo;
765 type DbWeight: Get<crate::weights::RuntimeDbWeight>;
766 }
767
768 #[pallet::error]
769 pub enum Error<T> {
770 CallFiltered,
772 }
773
774 #[pallet::origin]
775 pub type Origin<T> = RawOrigin<<T as Config>::AccountId>;
776
777 #[pallet::call]
778 impl<T: Config> Pallet<T> {
779 #[pallet::weight(1000)]
781 pub fn f00(_origin: OriginFor<T>) -> DispatchResult {
782 unimplemented!();
783 }
784
785 #[pallet::weight((1000, DispatchClass::Mandatory))]
786 pub fn f01(_origin: OriginFor<T>) -> DispatchResult {
787 unimplemented!();
788 }
789
790 #[pallet::weight((1000, Pays::No))]
791 pub fn f02(_origin: OriginFor<T>) -> DispatchResult {
792 unimplemented!();
793 }
794
795 #[pallet::weight((1000, DispatchClass::Operational, Pays::No))]
796 pub fn f03(_origin: OriginFor<T>) -> DispatchResult {
797 unimplemented!();
798 }
799
800 #[pallet::weight(((_a * 10 + _eb * 1) as u64, DispatchClass::Normal, Pays::Yes))]
802 pub fn f11(_origin: OriginFor<T>, _a: u32, _eb: u32) -> DispatchResult {
803 unimplemented!();
804 }
805
806 #[pallet::weight((0, DispatchClass::Operational, Pays::Yes))]
807 pub fn f12(_origin: OriginFor<T>, _a: u32, _eb: u32) -> DispatchResult {
808 unimplemented!();
809 }
810
811 #[pallet::weight(T::DbWeight::get().reads(3) + T::DbWeight::get().writes(2) + Weight::from_all(10_000))]
812 pub fn f20(_origin: OriginFor<T>) -> DispatchResult {
813 unimplemented!();
814 }
815
816 #[pallet::weight(T::DbWeight::get().reads_writes(6, 5) + Weight::from_all(40_000))]
817 pub fn f21(_origin: OriginFor<T>) -> DispatchResult {
818 unimplemented!();
819 }
820
821 #[pallet::weight(1000)]
822 pub fn f99(_origin: OriginFor<T>) -> DispatchResult {
823 Ok(())
824 }
825
826 #[pallet::weight(1000)]
827 pub fn f100(_origin: OriginFor<T>) -> DispatchResultWithPostInfo {
828 Ok(crate::dispatch::PostDispatchInfo {
829 actual_weight: Some(Weight::from_parts(500, 0)),
830 pays_fee: Pays::Yes,
831 })
832 }
833 }
834
835 pub mod pallet_prelude {
836 pub type OriginFor<T> = <T as super::Config>::RuntimeOrigin;
837
838 pub type HeaderFor<T> =
839 <<T as super::Config>::Block as sp_runtime::traits::HeaderProvider>::HeaderT;
840
841 pub type BlockNumberFor<T> = <HeaderFor<T> as sp_runtime::traits::Header>::Number;
842 }
843 }
844
845 type BlockNumber = u32;
846 type AccountId = u32;
847 type Balance = u32;
848 type Header = generic::Header<BlockNumber, BlakeTwo256>;
849 type UncheckedExtrinsic = generic::UncheckedExtrinsic<u32, RuntimeCall, (), ()>;
850 type Block = generic::Block<Header, UncheckedExtrinsic>;
851
852 crate::construct_runtime!(
853 pub enum Runtime
854 {
855 System: self::frame_system,
856 }
857 );
858
859 parameter_types! {
860 pub const DbWeight: RuntimeDbWeight = RuntimeDbWeight {
861 read: 100,
862 write: 1000,
863 };
864 }
865
866 impl Config for Runtime {
867 type Block = Block;
868 type AccountId = AccountId;
869 type Balance = Balance;
870 type BaseCallFilter = crate::traits::Everything;
871 type RuntimeOrigin = RuntimeOrigin;
872 type RuntimeCall = RuntimeCall;
873 type RuntimeTask = RuntimeTask;
874 type DbWeight = DbWeight;
875 type PalletInfo = PalletInfo;
876 }
877
878 #[test]
879 fn weights_are_correct() {
880 let info = Call::<Runtime>::f00 {}.get_dispatch_info();
882 assert_eq!(info.total_weight(), Weight::from_parts(1000, 0));
883 assert_eq!(info.class, DispatchClass::Normal);
884 assert_eq!(info.pays_fee, Pays::Yes);
885
886 let info = Call::<Runtime>::f01 {}.get_dispatch_info();
888 assert_eq!(info.total_weight(), Weight::from_parts(1000, 0));
889 assert_eq!(info.class, DispatchClass::Mandatory);
890 assert_eq!(info.pays_fee, Pays::Yes);
891
892 let info = Call::<Runtime>::f02 {}.get_dispatch_info();
894 assert_eq!(info.total_weight(), Weight::from_parts(1000, 0));
895 assert_eq!(info.class, DispatchClass::Normal);
896 assert_eq!(info.pays_fee, Pays::No);
897
898 let info = Call::<Runtime>::f03 {}.get_dispatch_info();
900 assert_eq!(info.total_weight(), Weight::from_parts(1000, 0));
901 assert_eq!(info.class, DispatchClass::Operational);
902 assert_eq!(info.pays_fee, Pays::No);
903
904 let info = Call::<Runtime>::f11 { a: 13, eb: 20 }.get_dispatch_info();
906 assert_eq!(info.total_weight(), Weight::from_parts(150, 0)); assert_eq!(info.class, DispatchClass::Normal);
908 assert_eq!(info.pays_fee, Pays::Yes);
909
910 let info = Call::<Runtime>::f12 { a: 10, eb: 20 }.get_dispatch_info();
912 assert_eq!(info.total_weight(), Weight::zero());
913 assert_eq!(info.class, DispatchClass::Operational);
914 assert_eq!(info.pays_fee, Pays::Yes);
915
916 let info = Call::<Runtime>::f20 {}.get_dispatch_info();
919 assert_eq!(info.total_weight(), Weight::from_parts(12300, 10000)); assert_eq!(info.class, DispatchClass::Normal);
921 assert_eq!(info.pays_fee, Pays::Yes);
922
923 let info = Call::<Runtime>::f21 {}.get_dispatch_info();
925 assert_eq!(info.total_weight(), Weight::from_parts(45600, 40000)); assert_eq!(info.class, DispatchClass::Normal);
927 assert_eq!(info.pays_fee, Pays::Yes);
928 }
929
930 #[test]
931 fn extract_actual_weight_works() {
932 let pre = DispatchInfo {
933 call_weight: Weight::from_parts(1000, 0),
934 extension_weight: Weight::zero(),
935 ..Default::default()
936 };
937 assert_eq!(
938 extract_actual_weight(&Ok(from_actual_ref_time(Some(7))), &pre),
939 Weight::from_parts(7, 0)
940 );
941 assert_eq!(
942 extract_actual_weight(&Ok(from_actual_ref_time(Some(1000))), &pre),
943 Weight::from_parts(1000, 0)
944 );
945 assert_eq!(
946 extract_actual_weight(
947 &Err(DispatchError::BadOrigin.with_weight(Weight::from_parts(9, 0))),
948 &pre
949 ),
950 Weight::from_parts(9, 0)
951 );
952 }
953
954 #[test]
955 fn extract_actual_weight_caps_at_pre_weight() {
956 let pre = DispatchInfo {
957 call_weight: Weight::from_parts(1000, 0),
958 extension_weight: Weight::zero(),
959 ..Default::default()
960 };
961 assert_eq!(
962 extract_actual_weight(&Ok(from_actual_ref_time(Some(1250))), &pre),
963 Weight::from_parts(1000, 0)
964 );
965 assert_eq!(
966 extract_actual_weight(
967 &Err(DispatchError::BadOrigin.with_weight(Weight::from_parts(1300, 0))),
968 &pre
969 ),
970 Weight::from_parts(1000, 0),
971 );
972 }
973
974 #[test]
975 fn extract_actual_pays_fee_works() {
976 let pre = DispatchInfo {
977 call_weight: Weight::from_parts(1000, 0),
978 extension_weight: Weight::zero(),
979 ..Default::default()
980 };
981 assert_eq!(extract_actual_pays_fee(&Ok(from_actual_ref_time(Some(7))), &pre), Pays::Yes);
982 assert_eq!(
983 extract_actual_pays_fee(&Ok(from_actual_ref_time(Some(1000)).into()), &pre),
984 Pays::Yes
985 );
986 assert_eq!(
987 extract_actual_pays_fee(&Ok(from_post_weight_info(Some(1000), Pays::Yes)), &pre),
988 Pays::Yes
989 );
990 assert_eq!(
991 extract_actual_pays_fee(&Ok(from_post_weight_info(Some(1000), Pays::No)), &pre),
992 Pays::No
993 );
994 assert_eq!(
995 extract_actual_pays_fee(
996 &Err(DispatchError::BadOrigin.with_weight(Weight::from_parts(9, 0))),
997 &pre
998 ),
999 Pays::Yes
1000 );
1001 assert_eq!(
1002 extract_actual_pays_fee(
1003 &Err(DispatchErrorWithPostInfo {
1004 post_info: PostDispatchInfo { actual_weight: None, pays_fee: Pays::No },
1005 error: DispatchError::BadOrigin,
1006 }),
1007 &pre
1008 ),
1009 Pays::No
1010 );
1011
1012 let pre = DispatchInfo {
1013 call_weight: Weight::from_parts(1000, 0),
1014 extension_weight: Weight::zero(),
1015 pays_fee: Pays::No,
1016 ..Default::default()
1017 };
1018 assert_eq!(extract_actual_pays_fee(&Ok(from_actual_ref_time(Some(7))), &pre), Pays::No);
1019 assert_eq!(extract_actual_pays_fee(&Ok(from_actual_ref_time(Some(1000))), &pre), Pays::No);
1020 assert_eq!(
1021 extract_actual_pays_fee(&Ok(from_post_weight_info(Some(1000), Pays::Yes)), &pre),
1022 Pays::No
1023 );
1024 }
1025
1026 #[test]
1027 fn weight_accrue_works() {
1028 let mut post_dispatch = PostDispatchInfo {
1029 actual_weight: Some(Weight::from_parts(1100, 25)),
1030 pays_fee: Pays::Yes,
1031 };
1032 post_dispatch.refund(Weight::from_parts(100, 15));
1033 assert_eq!(
1034 post_dispatch,
1035 PostDispatchInfo {
1036 actual_weight: Some(Weight::from_parts(1000, 10)),
1037 pays_fee: Pays::Yes
1038 }
1039 );
1040
1041 let mut post_dispatch = PostDispatchInfo { actual_weight: None, pays_fee: Pays::Yes };
1042 post_dispatch.refund(Weight::from_parts(100, 15));
1043 assert_eq!(post_dispatch, PostDispatchInfo { actual_weight: None, pays_fee: Pays::Yes });
1044 }
1045}
1046
1047#[cfg(test)]
1048mod per_dispatch_class_tests {
1049 use super::*;
1050 use sp_runtime::traits::Zero;
1051 use DispatchClass::*;
1052
1053 #[test]
1054 fn add_works() {
1055 let a = PerDispatchClass {
1056 normal: (5, 10).into(),
1057 operational: (20, 30).into(),
1058 mandatory: Weight::MAX,
1059 };
1060 assert_eq!(
1061 a.clone()
1062 .add((20, 5).into(), Normal)
1063 .add((10, 10).into(), Operational)
1064 .add((u64::MAX, 3).into(), Mandatory),
1065 PerDispatchClass {
1066 normal: (25, 15).into(),
1067 operational: (30, 40).into(),
1068 mandatory: Weight::MAX
1069 }
1070 );
1071 let b = a
1072 .add(Weight::MAX, Normal)
1073 .add(Weight::MAX, Operational)
1074 .add(Weight::MAX, Mandatory);
1075 assert_eq!(
1076 b,
1077 PerDispatchClass {
1078 normal: Weight::MAX,
1079 operational: Weight::MAX,
1080 mandatory: Weight::MAX
1081 }
1082 );
1083 assert_eq!(b.total(), Weight::MAX);
1084 }
1085
1086 #[test]
1087 fn accrue_works() {
1088 let mut a = PerDispatchClass::default();
1089
1090 a.accrue((10, 15).into(), Normal);
1091 assert_eq!(a.normal, (10, 15).into());
1092 assert_eq!(a.total(), (10, 15).into());
1093
1094 a.accrue((20, 25).into(), Operational);
1095 assert_eq!(a.operational, (20, 25).into());
1096 assert_eq!(a.total(), (30, 40).into());
1097
1098 a.accrue((30, 35).into(), Mandatory);
1099 assert_eq!(a.mandatory, (30, 35).into());
1100 assert_eq!(a.total(), (60, 75).into());
1101
1102 a.accrue((u64::MAX, 10).into(), Operational);
1103 assert_eq!(a.operational, (u64::MAX, 35).into());
1104 assert_eq!(a.total(), (u64::MAX, 85).into());
1105
1106 a.accrue((10, u64::MAX).into(), Normal);
1107 assert_eq!(a.normal, (20, u64::MAX).into());
1108 assert_eq!(a.total(), Weight::MAX);
1109 }
1110
1111 #[test]
1112 fn reduce_works() {
1113 let mut a = PerDispatchClass {
1114 normal: (10, u64::MAX).into(),
1115 mandatory: (u64::MAX, 10).into(),
1116 operational: (20, 20).into(),
1117 };
1118
1119 a.reduce((5, 100).into(), Normal);
1120 assert_eq!(a.normal, (5, u64::MAX - 100).into());
1121 assert_eq!(a.total(), (u64::MAX, u64::MAX - 70).into());
1122
1123 a.reduce((15, 5).into(), Operational);
1124 assert_eq!(a.operational, (5, 15).into());
1125 assert_eq!(a.total(), (u64::MAX, u64::MAX - 75).into());
1126
1127 a.reduce((50, 0).into(), Mandatory);
1128 assert_eq!(a.mandatory, (u64::MAX - 50, 10).into());
1129 assert_eq!(a.total(), (u64::MAX - 40, u64::MAX - 75).into());
1130
1131 a.reduce((u64::MAX, 100).into(), Operational);
1132 assert!(a.operational.is_zero());
1133 assert_eq!(a.total(), (u64::MAX - 45, u64::MAX - 90).into());
1134
1135 a.reduce((5, u64::MAX).into(), Normal);
1136 assert!(a.normal.is_zero());
1137 assert_eq!(a.total(), (u64::MAX - 50, 10).into());
1138 }
1139
1140 #[test]
1141 fn checked_accrue_works() {
1142 let mut a = PerDispatchClass::default();
1143
1144 a.checked_accrue((1, 2).into(), Normal).unwrap();
1145 a.checked_accrue((3, 4).into(), Operational).unwrap();
1146 a.checked_accrue((5, 6).into(), Mandatory).unwrap();
1147 a.checked_accrue((7, 8).into(), Operational).unwrap();
1148 a.checked_accrue((9, 0).into(), Normal).unwrap();
1149
1150 assert_eq!(
1151 a,
1152 PerDispatchClass {
1153 normal: (10, 2).into(),
1154 operational: (10, 12).into(),
1155 mandatory: (5, 6).into(),
1156 }
1157 );
1158
1159 a.checked_accrue((u64::MAX - 10, u64::MAX - 2).into(), Normal).unwrap();
1160 a.checked_accrue((0, 0).into(), Normal).unwrap();
1161 a.checked_accrue((1, 0).into(), Normal).unwrap_err();
1162 a.checked_accrue((0, 1).into(), Normal).unwrap_err();
1163
1164 assert_eq!(
1165 a,
1166 PerDispatchClass {
1167 normal: Weight::MAX,
1168 operational: (10, 12).into(),
1169 mandatory: (5, 6).into(),
1170 }
1171 );
1172 }
1173
1174 #[test]
1175 fn checked_accrue_does_not_modify_on_error() {
1176 let mut a = PerDispatchClass {
1177 normal: 0.into(),
1178 operational: Weight::MAX / 2 + 2.into(),
1179 mandatory: 10.into(),
1180 };
1181
1182 a.checked_accrue(Weight::MAX / 2, Operational).unwrap_err();
1183 a.checked_accrue(Weight::MAX - 9.into(), Mandatory).unwrap_err();
1184 a.checked_accrue(Weight::MAX, Normal).unwrap(); assert_eq!(
1187 a,
1188 PerDispatchClass {
1189 normal: Weight::MAX,
1190 operational: Weight::MAX / 2 + 2.into(),
1191 mandatory: 10.into(),
1192 }
1193 );
1194 }
1195
1196 #[test]
1197 fn total_works() {
1198 assert!(PerDispatchClass::default().total().is_zero());
1199
1200 assert_eq!(
1201 PerDispatchClass {
1202 normal: 0.into(),
1203 operational: (10, 20).into(),
1204 mandatory: (20, u64::MAX).into(),
1205 }
1206 .total(),
1207 (30, u64::MAX).into()
1208 );
1209
1210 assert_eq!(
1211 PerDispatchClass {
1212 normal: (u64::MAX - 10, 10).into(),
1213 operational: (3, u64::MAX).into(),
1214 mandatory: (4, u64::MAX).into(),
1215 }
1216 .total(),
1217 (u64::MAX - 3, u64::MAX).into()
1218 );
1219 }
1220}
1221
1222#[cfg(test)]
1223mod test_extensions {
1224 use codec::{Decode, DecodeWithMemTracking, Encode};
1225 use scale_info::TypeInfo;
1226 use sp_runtime::{
1227 impl_tx_ext_default,
1228 traits::{
1229 DispatchInfoOf, DispatchOriginOf, Dispatchable, PostDispatchInfoOf,
1230 TransactionExtension,
1231 },
1232 transaction_validity::TransactionValidityError,
1233 };
1234 use sp_weights::Weight;
1235
1236 use super::{DispatchResult, PostDispatchInfo};
1237
1238 #[derive(Clone, Eq, PartialEq, Debug, Encode, Decode, DecodeWithMemTracking, TypeInfo)]
1240 pub struct HalfCostIf(pub bool);
1241
1242 impl<RuntimeCall: Dispatchable> TransactionExtension<RuntimeCall> for HalfCostIf {
1243 const IDENTIFIER: &'static str = "HalfCostIf";
1244 type Implicit = ();
1245 type Val = ();
1246 type Pre = bool;
1247
1248 fn weight(&self, _: &RuntimeCall) -> sp_weights::Weight {
1249 Weight::from_parts(100, 0)
1250 }
1251
1252 fn prepare(
1253 self,
1254 _val: Self::Val,
1255 _origin: &DispatchOriginOf<RuntimeCall>,
1256 _call: &RuntimeCall,
1257 _info: &DispatchInfoOf<RuntimeCall>,
1258 _len: usize,
1259 ) -> Result<Self::Pre, TransactionValidityError> {
1260 Ok(self.0)
1261 }
1262
1263 fn post_dispatch_details(
1264 pre: Self::Pre,
1265 _info: &DispatchInfoOf<RuntimeCall>,
1266 _post_info: &PostDispatchInfoOf<RuntimeCall>,
1267 _len: usize,
1268 _result: &DispatchResult,
1269 ) -> Result<Weight, TransactionValidityError> {
1270 if pre {
1271 Ok(Weight::from_parts(50, 0))
1272 } else {
1273 Ok(Weight::zero())
1274 }
1275 }
1276 impl_tx_ext_default!(RuntimeCall; validate);
1277 }
1278
1279 #[derive(Clone, Eq, PartialEq, Debug, Encode, Decode, DecodeWithMemTracking, TypeInfo)]
1282 pub struct FreeIfUnder(pub u64);
1283
1284 impl<RuntimeCall: Dispatchable> TransactionExtension<RuntimeCall> for FreeIfUnder
1285 where
1286 RuntimeCall: Dispatchable<PostInfo = PostDispatchInfo>,
1287 {
1288 const IDENTIFIER: &'static str = "FreeIfUnder";
1289 type Implicit = ();
1290 type Val = ();
1291 type Pre = u64;
1292
1293 fn weight(&self, _: &RuntimeCall) -> sp_weights::Weight {
1294 Weight::from_parts(200, 0)
1295 }
1296
1297 fn prepare(
1298 self,
1299 _val: Self::Val,
1300 _origin: &DispatchOriginOf<RuntimeCall>,
1301 _call: &RuntimeCall,
1302 _info: &DispatchInfoOf<RuntimeCall>,
1303 _len: usize,
1304 ) -> Result<Self::Pre, TransactionValidityError> {
1305 Ok(self.0)
1306 }
1307
1308 fn post_dispatch_details(
1309 pre: Self::Pre,
1310 _info: &DispatchInfoOf<RuntimeCall>,
1311 post_info: &PostDispatchInfoOf<RuntimeCall>,
1312 _len: usize,
1313 _result: &DispatchResult,
1314 ) -> Result<Weight, TransactionValidityError> {
1315 if let Some(actual) = post_info.actual_weight {
1316 if pre > actual.ref_time() {
1317 return Ok(Weight::from_parts(200, 0));
1318 }
1319 }
1320 Ok(Weight::zero())
1321 }
1322 impl_tx_ext_default!(RuntimeCall; validate);
1323 }
1324
1325 #[derive(Clone, Eq, PartialEq, Debug, Encode, Decode, DecodeWithMemTracking, TypeInfo)]
1328 pub struct ActualWeightIs(pub u64);
1329
1330 impl<RuntimeCall: Dispatchable> TransactionExtension<RuntimeCall> for ActualWeightIs {
1331 const IDENTIFIER: &'static str = "ActualWeightIs";
1332 type Implicit = ();
1333 type Val = ();
1334 type Pre = u64;
1335
1336 fn weight(&self, _: &RuntimeCall) -> sp_weights::Weight {
1337 Weight::from_parts(300, 0)
1338 }
1339
1340 fn prepare(
1341 self,
1342 _val: Self::Val,
1343 _origin: &DispatchOriginOf<RuntimeCall>,
1344 _call: &RuntimeCall,
1345 _info: &DispatchInfoOf<RuntimeCall>,
1346 _len: usize,
1347 ) -> Result<Self::Pre, TransactionValidityError> {
1348 Ok(self.0)
1349 }
1350
1351 fn post_dispatch_details(
1352 pre: Self::Pre,
1353 _info: &DispatchInfoOf<RuntimeCall>,
1354 _post_info: &PostDispatchInfoOf<RuntimeCall>,
1355 _len: usize,
1356 _result: &DispatchResult,
1357 ) -> Result<Weight, TransactionValidityError> {
1358 Ok(Weight::from_parts(300u64.saturating_sub(pre), 0))
1359 }
1360 impl_tx_ext_default!(RuntimeCall; validate);
1361 }
1362}
1363
1364#[cfg(test)]
1365#[allow(dead_code)]
1367mod extension_weight_tests {
1368 use crate::assert_ok;
1369
1370 use super::*;
1371 use sp_core::parameter_types;
1372 use sp_runtime::{
1373 generic::{self, ExtrinsicFormat},
1374 traits::{Applyable, BlakeTwo256, DispatchTransaction, TransactionExtension},
1375 };
1376 use sp_weights::RuntimeDbWeight;
1377 use test_extensions::{ActualWeightIs, FreeIfUnder, HalfCostIf};
1378
1379 use super::weight_tests::frame_system;
1380 use frame_support::construct_runtime;
1381
1382 pub type TxExtension = (HalfCostIf, FreeIfUnder, ActualWeightIs);
1383 pub type UncheckedExtrinsic = generic::UncheckedExtrinsic<u64, RuntimeCall, (), TxExtension>;
1384 pub type Header = generic::Header<BlockNumber, BlakeTwo256>;
1385 pub type Block = generic::Block<Header, UncheckedExtrinsic>;
1386 pub type AccountId = u64;
1387 pub type Balance = u32;
1388 pub type BlockNumber = u32;
1389
1390 construct_runtime!(
1391 pub enum ExtRuntime {
1392 System: frame_system,
1393 }
1394 );
1395
1396 impl frame_system::Config for ExtRuntime {
1397 type Block = Block;
1398 type AccountId = AccountId;
1399 type Balance = Balance;
1400 type BaseCallFilter = crate::traits::Everything;
1401 type RuntimeOrigin = RuntimeOrigin;
1402 type RuntimeCall = RuntimeCall;
1403 type RuntimeTask = RuntimeTask;
1404 type DbWeight = DbWeight;
1405 type PalletInfo = PalletInfo;
1406 }
1407
1408 parameter_types! {
1409 pub const DbWeight: RuntimeDbWeight = RuntimeDbWeight {
1410 read: 100,
1411 write: 1000,
1412 };
1413 }
1414
1415 pub struct ExtBuilder {}
1416
1417 impl Default for ExtBuilder {
1418 fn default() -> Self {
1419 Self {}
1420 }
1421 }
1422
1423 impl ExtBuilder {
1424 pub fn build(self) -> sp_io::TestExternalities {
1425 let mut ext = sp_io::TestExternalities::new(Default::default());
1426 ext.execute_with(|| {});
1427 ext
1428 }
1429
1430 pub fn build_and_execute(self, test: impl FnOnce() -> ()) {
1431 self.build().execute_with(|| {
1432 test();
1433 })
1434 }
1435 }
1436
1437 #[test]
1438 fn no_post_dispatch_with_no_refund() {
1439 ExtBuilder::default().build_and_execute(|| {
1440 let call = RuntimeCall::System(frame_system::Call::<ExtRuntime>::f99 {});
1441 let ext: TxExtension = (HalfCostIf(false), FreeIfUnder(1500), ActualWeightIs(0));
1442 let uxt = UncheckedExtrinsic::new_signed(call.clone(), 0, (), ext.clone());
1443 assert_eq!(uxt.extension_weight(), Weight::from_parts(600, 0));
1444
1445 let mut info = call.get_dispatch_info();
1446 assert_eq!(info.total_weight(), Weight::from_parts(1000, 0));
1447 info.extension_weight = ext.weight(&call);
1448 let (pre, _) = ext.validate_and_prepare(Some(0).into(), &call, &info, 0, 0).unwrap();
1449 let res = call.dispatch(Some(0).into());
1450 let mut post_info = res.unwrap();
1451 assert!(post_info.actual_weight.is_none());
1452 assert_ok!(<TxExtension as TransactionExtension<RuntimeCall>>::post_dispatch(
1453 pre,
1454 &info,
1455 &mut post_info,
1456 0,
1457 &Ok(()),
1458 ));
1459 assert!(post_info.actual_weight.is_none());
1460 });
1461 }
1462
1463 #[test]
1464 fn no_post_dispatch_refunds_when_dispatched() {
1465 ExtBuilder::default().build_and_execute(|| {
1466 let call = RuntimeCall::System(frame_system::Call::<ExtRuntime>::f99 {});
1467 let ext: TxExtension = (HalfCostIf(true), FreeIfUnder(100), ActualWeightIs(0));
1468 let uxt = UncheckedExtrinsic::new_signed(call.clone(), 0, (), ext.clone());
1469 assert_eq!(uxt.extension_weight(), Weight::from_parts(600, 0));
1470
1471 let mut info = call.get_dispatch_info();
1472 assert_eq!(info.total_weight(), Weight::from_parts(1000, 0));
1473 info.extension_weight = ext.weight(&call);
1474 let post_info =
1475 ext.dispatch_transaction(Some(0).into(), call, &info, 0, 0).unwrap().unwrap();
1476 assert_eq!(post_info.actual_weight, Some(Weight::from_parts(1250, 0)));
1478 });
1479 }
1480
1481 #[test]
1482 fn post_dispatch_with_refunds() {
1483 ExtBuilder::default().build_and_execute(|| {
1484 let call = RuntimeCall::System(frame_system::Call::<ExtRuntime>::f100 {});
1485 let ext: TxExtension = (HalfCostIf(false), FreeIfUnder(2000), ActualWeightIs(0));
1487 let uxt = UncheckedExtrinsic::new_signed(call.clone(), 0, (), ext.clone());
1488 assert_eq!(uxt.extension_weight(), Weight::from_parts(600, 0));
1489
1490 let mut info = call.get_dispatch_info();
1491 assert_eq!(info.call_weight, Weight::from_parts(1000, 0));
1492 info.extension_weight = ext.weight(&call);
1493 assert_eq!(info.total_weight(), Weight::from_parts(1600, 0));
1494 let (pre, _) = ext.validate_and_prepare(Some(0).into(), &call, &info, 0, 0).unwrap();
1495 let res = call.clone().dispatch(Some(0).into());
1496 let mut post_info = res.unwrap();
1497 assert_eq!(post_info.actual_weight, Some(Weight::from_parts(500, 0)));
1499 post_info.set_extension_weight(&info);
1501 assert_ok!(<TxExtension as TransactionExtension<RuntimeCall>>::post_dispatch(
1503 pre,
1504 &info,
1505 &mut post_info,
1506 0,
1507 &Ok(()),
1508 ));
1509 assert_eq!(post_info.actual_weight, Some(Weight::from_parts(600, 0)));
1511
1512 let ext: TxExtension = (HalfCostIf(false), FreeIfUnder(1100), ActualWeightIs(200));
1514 let (pre, _) = ext.validate_and_prepare(Some(0).into(), &call, &info, 0, 0).unwrap();
1515 let res = call.clone().dispatch(Some(0).into());
1516 let mut post_info = res.unwrap();
1517 assert_eq!(post_info.actual_weight, Some(Weight::from_parts(500, 0)));
1519 post_info.set_extension_weight(&info);
1521 assert_ok!(<TxExtension as TransactionExtension<RuntimeCall>>::post_dispatch(
1523 pre,
1524 &info,
1525 &mut post_info,
1526 0,
1527 &Ok(()),
1528 ));
1529 assert_eq!(post_info.actual_weight, Some(Weight::from_parts(1000, 0)));
1531
1532 let ext: TxExtension = (HalfCostIf(true), FreeIfUnder(1060), ActualWeightIs(200));
1534 let (pre, _) = ext.validate_and_prepare(Some(0).into(), &call, &info, 0, 0).unwrap();
1535 let res = call.clone().dispatch(Some(0).into());
1536 let mut post_info = res.unwrap();
1537 assert_eq!(post_info.actual_weight, Some(Weight::from_parts(500, 0)));
1539 post_info.set_extension_weight(&info);
1541 assert_ok!(<TxExtension as TransactionExtension<RuntimeCall>>::post_dispatch(
1543 pre,
1544 &info,
1545 &mut post_info,
1546 0,
1547 &Ok(()),
1548 ));
1549 assert_eq!(post_info.actual_weight, Some(Weight::from_parts(750, 0)));
1551
1552 let ext: TxExtension = (HalfCostIf(false), FreeIfUnder(100), ActualWeightIs(300));
1554 let (pre, _) = ext.validate_and_prepare(Some(0).into(), &call, &info, 0, 0).unwrap();
1555 let res = call.clone().dispatch(Some(0).into());
1556 let mut post_info = res.unwrap();
1557 assert_eq!(post_info.actual_weight, Some(Weight::from_parts(500, 0)));
1559 post_info.set_extension_weight(&info);
1561 assert_ok!(<TxExtension as TransactionExtension<RuntimeCall>>::post_dispatch(
1563 pre,
1564 &info,
1565 &mut post_info,
1566 0,
1567 &Ok(()),
1568 ));
1569 assert_eq!(post_info.actual_weight, Some(Weight::from_parts(1100, 0)));
1571 });
1572 }
1573
1574 #[test]
1575 fn checked_extrinsic_apply() {
1576 ExtBuilder::default().build_and_execute(|| {
1577 let call = RuntimeCall::System(frame_system::Call::<ExtRuntime>::f100 {});
1578 let ext: TxExtension = (HalfCostIf(false), FreeIfUnder(2000), ActualWeightIs(0));
1580 let xt = CheckedExtrinsic {
1581 format: ExtrinsicFormat::Signed(0, ext.clone()),
1582 function: call.clone(),
1583 };
1584 assert_eq!(xt.extension_weight(), Weight::from_parts(600, 0));
1585 let mut info = call.get_dispatch_info();
1586 assert_eq!(info.call_weight, Weight::from_parts(1000, 0));
1587 info.extension_weight = ext.weight(&call);
1588 assert_eq!(info.total_weight(), Weight::from_parts(1600, 0));
1589 let post_info = xt.apply::<ExtRuntime>(&info, 0).unwrap().unwrap();
1590 assert_eq!(post_info.actual_weight, Some(Weight::from_parts(600, 0)));
1592
1593 let ext: TxExtension = (HalfCostIf(false), FreeIfUnder(1100), ActualWeightIs(200));
1595 let xt = CheckedExtrinsic {
1596 format: ExtrinsicFormat::Signed(0, ext),
1597 function: call.clone(),
1598 };
1599 let post_info = xt.apply::<ExtRuntime>(&info, 0).unwrap().unwrap();
1600 assert_eq!(post_info.actual_weight, Some(Weight::from_parts(1000, 0)));
1602
1603 let ext: TxExtension = (HalfCostIf(true), FreeIfUnder(1060), ActualWeightIs(200));
1605 let xt = CheckedExtrinsic {
1606 format: ExtrinsicFormat::Signed(0, ext),
1607 function: call.clone(),
1608 };
1609 let post_info = xt.apply::<ExtRuntime>(&info, 0).unwrap().unwrap();
1610 assert_eq!(post_info.actual_weight, Some(Weight::from_parts(750, 0)));
1612
1613 let ext: TxExtension = (HalfCostIf(false), FreeIfUnder(100), ActualWeightIs(300));
1615 let xt = CheckedExtrinsic {
1616 format: ExtrinsicFormat::Signed(0, ext),
1617 function: call.clone(),
1618 };
1619 let post_info = xt.apply::<ExtRuntime>(&info, 0).unwrap().unwrap();
1620 assert_eq!(post_info.actual_weight, Some(Weight::from_parts(1100, 0)));
1622 });
1623 }
1624}