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