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
365pub trait WithPostDispatchInfo {
368 fn with_weight(self, actual_weight: Weight) -> DispatchErrorWithPostInfo;
377}
378
379impl<T> WithPostDispatchInfo for T
380where
381 T: Into<DispatchError>,
382{
383 fn with_weight(self, actual_weight: Weight) -> DispatchErrorWithPostInfo {
384 DispatchErrorWithPostInfo {
385 post_info: PostDispatchInfo {
386 actual_weight: Some(actual_weight),
387 pays_fee: Default::default(),
388 },
389 error: self.into(),
390 }
391 }
392}
393
394impl<Address, Call, Signature, ExtensionV0, ExtensionOtherVersions> GetDispatchInfo
396 for UncheckedExtrinsic<Address, Call, Signature, ExtensionV0, ExtensionOtherVersions>
397where
398 Call: GetDispatchInfo + Dispatchable + Encode,
399 ExtensionV0: TransactionExtension<Call>,
400 ExtensionOtherVersions: sp_runtime::traits::Pipeline<Call>,
401 <Call as Dispatchable>::RuntimeOrigin: sp_runtime::traits::AsTransactionAuthorizedOrigin,
402{
403 fn get_dispatch_info(&self) -> DispatchInfo {
404 let mut info = self.function.get_dispatch_info();
405 info.extension_weight = self.extension_weight();
406 info
407 }
408}
409
410impl<AccountId, Call, ExtensionV0, ExtensionOtherVersions> GetDispatchInfo
412 for CheckedExtrinsic<AccountId, Call, ExtensionV0, ExtensionOtherVersions>
413where
414 Call: GetDispatchInfo + Dispatchable + Encode,
415 ExtensionV0: TransactionExtension<Call>,
416 ExtensionOtherVersions: sp_runtime::traits::Pipeline<Call>,
417 <Call as Dispatchable>::RuntimeOrigin: sp_runtime::traits::AsTransactionAuthorizedOrigin,
418{
419 fn get_dispatch_info(&self) -> DispatchInfo {
420 let mut info = self.function.get_dispatch_info();
421 info.extension_weight = self.extension_weight();
422 info
423 }
424}
425
426#[derive(Clone, Eq, PartialEq, Default, Debug, Encode, Decode, TypeInfo, MaxEncodedLen)]
428pub struct PerDispatchClass<T> {
429 normal: T,
431 operational: T,
433 mandatory: T,
435}
436
437impl<T> PerDispatchClass<T> {
438 pub fn new(val: impl Fn(DispatchClass) -> T) -> Self {
440 Self {
441 normal: val(DispatchClass::Normal),
442 operational: val(DispatchClass::Operational),
443 mandatory: val(DispatchClass::Mandatory),
444 }
445 }
446
447 pub fn get_mut(&mut self, class: DispatchClass) -> &mut T {
449 match class {
450 DispatchClass::Operational => &mut self.operational,
451 DispatchClass::Normal => &mut self.normal,
452 DispatchClass::Mandatory => &mut self.mandatory,
453 }
454 }
455
456 pub fn get(&self, class: DispatchClass) -> &T {
458 match class {
459 DispatchClass::Normal => &self.normal,
460 DispatchClass::Operational => &self.operational,
461 DispatchClass::Mandatory => &self.mandatory,
462 }
463 }
464}
465
466impl<T: Clone> PerDispatchClass<T> {
467 pub fn set(&mut self, new: T, class: impl OneOrMany<DispatchClass>) {
469 for class in class.into_iter() {
470 *self.get_mut(class) = new.clone();
471 }
472 }
473}
474
475impl PerDispatchClass<Weight> {
476 pub fn total(&self) -> Weight {
480 let mut sum = Weight::zero();
481 for class in DispatchClass::all() {
482 sum.saturating_accrue(*self.get(*class));
483 }
484 sum
485 }
486
487 pub fn add(mut self, weight: Weight, class: DispatchClass) -> Self {
489 self.accrue(weight, class);
490 self
491 }
492
493 pub fn accrue(&mut self, weight: Weight, class: DispatchClass) {
495 self.get_mut(class).saturating_accrue(weight);
496 }
497
498 pub fn checked_accrue(&mut self, weight: Weight, class: DispatchClass) -> Result<(), ()> {
500 self.get_mut(class).checked_accrue(weight).ok_or(())
501 }
502
503 pub fn reduce(&mut self, weight: Weight, class: DispatchClass) {
505 self.get_mut(class).saturating_reduce(weight);
506 }
507}
508
509pub trait WeighData<T> {
511 fn weigh_data(&self, target: T) -> Weight;
514}
515
516impl<T> WeighData<T> for Weight {
517 fn weigh_data(&self, _: T) -> Weight {
518 return *self;
519 }
520}
521
522impl<T> PaysFee<T> for (Weight, DispatchClass, Pays) {
523 fn pays_fee(&self, _: T) -> Pays {
524 self.2
525 }
526}
527
528impl<T> WeighData<T> for (Weight, DispatchClass) {
529 fn weigh_data(&self, args: T) -> Weight {
530 return self.0.weigh_data(args);
531 }
532}
533
534impl<T> WeighData<T> for (Weight, DispatchClass, Pays) {
535 fn weigh_data(&self, args: T) -> Weight {
536 return self.0.weigh_data(args);
537 }
538}
539
540impl<T> ClassifyDispatch<T> for (Weight, DispatchClass) {
541 fn classify_dispatch(&self, _: T) -> DispatchClass {
542 self.1
543 }
544}
545
546impl<T> PaysFee<T> for (Weight, DispatchClass) {
547 fn pays_fee(&self, _: T) -> Pays {
548 Pays::Yes
549 }
550}
551
552impl<T> WeighData<T> for (Weight, Pays) {
553 fn weigh_data(&self, args: T) -> Weight {
554 return self.0.weigh_data(args);
555 }
556}
557
558impl<T> ClassifyDispatch<T> for (Weight, Pays) {
559 fn classify_dispatch(&self, _: T) -> DispatchClass {
560 DispatchClass::Normal
561 }
562}
563
564impl<T> PaysFee<T> for (Weight, Pays) {
565 fn pays_fee(&self, _: T) -> Pays {
566 self.1
567 }
568}
569
570impl From<(Option<Weight>, Pays)> for PostDispatchInfo {
571 fn from(post_weight_info: (Option<Weight>, Pays)) -> Self {
572 let (actual_weight, pays_fee) = post_weight_info;
573 Self { actual_weight, pays_fee }
574 }
575}
576
577impl From<Option<Weight>> for PostDispatchInfo {
578 fn from(actual_weight: Option<Weight>) -> Self {
579 Self { actual_weight, pays_fee: Default::default() }
580 }
581}
582
583impl<T> ClassifyDispatch<T> for Weight {
584 fn classify_dispatch(&self, _: T) -> DispatchClass {
585 DispatchClass::Normal
586 }
587}
588
589impl<T> PaysFee<T> for Weight {
590 fn pays_fee(&self, _: T) -> Pays {
591 Pays::Yes
592 }
593}
594
595impl<T> ClassifyDispatch<T> for (Weight, DispatchClass, Pays) {
596 fn classify_dispatch(&self, _: T) -> DispatchClass {
597 self.1
598 }
599}
600
601impl RefundWeight for PostDispatchInfo {
602 fn refund(&mut self, weight: Weight) {
603 if let Some(actual_weight) = self.actual_weight.as_mut() {
604 actual_weight.saturating_reduce(weight);
605 }
606 }
607}
608
609impl ExtensionPostDispatchWeightHandler<DispatchInfo> for PostDispatchInfo {
610 fn set_extension_weight(&mut self, info: &DispatchInfo) {
611 let actual_weight = self
612 .actual_weight
613 .unwrap_or(info.call_weight)
614 .saturating_add(info.extension_weight);
615 self.actual_weight = Some(actual_weight);
616 }
617}
618
619impl ExtensionPostDispatchWeightHandler<()> for PostDispatchInfo {
620 fn set_extension_weight(&mut self, _: &()) {}
621}
622
623impl<T> ClassifyDispatch<T> for u64 {
626 fn classify_dispatch(&self, _: T) -> DispatchClass {
627 DispatchClass::Normal
628 }
629}
630
631impl<T> PaysFee<T> for u64 {
632 fn pays_fee(&self, _: T) -> Pays {
633 Pays::Yes
634 }
635}
636
637impl<T> WeighData<T> for u64 {
638 fn weigh_data(&self, _: T) -> Weight {
639 return Weight::from_parts(*self, 0);
640 }
641}
642
643impl<T> WeighData<T> for (u64, DispatchClass, Pays) {
644 fn weigh_data(&self, args: T) -> Weight {
645 return self.0.weigh_data(args);
646 }
647}
648
649impl<T> ClassifyDispatch<T> for (u64, DispatchClass, Pays) {
650 fn classify_dispatch(&self, _: T) -> DispatchClass {
651 self.1
652 }
653}
654
655impl<T> PaysFee<T> for (u64, DispatchClass, Pays) {
656 fn pays_fee(&self, _: T) -> Pays {
657 self.2
658 }
659}
660
661impl<T> WeighData<T> for (u64, DispatchClass) {
662 fn weigh_data(&self, args: T) -> Weight {
663 return self.0.weigh_data(args);
664 }
665}
666
667impl<T> ClassifyDispatch<T> for (u64, DispatchClass) {
668 fn classify_dispatch(&self, _: T) -> DispatchClass {
669 self.1
670 }
671}
672
673impl<T> PaysFee<T> for (u64, DispatchClass) {
674 fn pays_fee(&self, _: T) -> Pays {
675 Pays::Yes
676 }
677}
678
679impl<T> WeighData<T> for (u64, Pays) {
680 fn weigh_data(&self, args: T) -> Weight {
681 return self.0.weigh_data(args);
682 }
683}
684
685impl<T> ClassifyDispatch<T> for (u64, Pays) {
686 fn classify_dispatch(&self, _: T) -> DispatchClass {
687 DispatchClass::Normal
688 }
689}
690
691impl<T> PaysFee<T> for (u64, Pays) {
692 fn pays_fee(&self, _: T) -> Pays {
693 self.1
694 }
695}
696
697#[cfg(test)]
700#[allow(dead_code)]
702mod weight_tests {
703 use super::*;
704 use sp_core::parameter_types;
705 use sp_runtime::{generic, traits::BlakeTwo256};
706 use sp_weights::RuntimeDbWeight;
707
708 pub use self::frame_system::{Call, Config};
709
710 fn from_actual_ref_time(ref_time: Option<u64>) -> PostDispatchInfo {
711 PostDispatchInfo {
712 actual_weight: ref_time.map(|t| Weight::from_all(t)),
713 pays_fee: Default::default(),
714 }
715 }
716
717 fn from_post_weight_info(ref_time: Option<u64>, pays_fee: Pays) -> PostDispatchInfo {
718 PostDispatchInfo { actual_weight: ref_time.map(|t| Weight::from_all(t)), pays_fee }
719 }
720
721 #[crate::pallet(dev_mode)]
722 pub mod frame_system {
723 use super::{frame_system, frame_system::pallet_prelude::*};
724 pub use crate::dispatch::RawOrigin;
725 use crate::pallet_prelude::*;
726
727 #[pallet::pallet]
728 pub struct Pallet<T>(_);
729
730 #[pallet::config]
731 #[pallet::disable_frame_system_supertrait_check]
732 pub trait Config: 'static {
733 type Block: Parameter + sp_runtime::traits::Block;
734 type AccountId;
735 type Balance;
736 type BaseCallFilter: crate::traits::Contains<Self::RuntimeCall>;
737 type RuntimeOrigin;
738 type RuntimeCall;
739 type RuntimeTask;
740 type PalletInfo: crate::traits::PalletInfo;
741 type DbWeight: Get<crate::weights::RuntimeDbWeight>;
742 }
743
744 #[pallet::error]
745 pub enum Error<T> {
746 CallFiltered,
748 }
749
750 #[pallet::origin]
751 pub type Origin<T> = RawOrigin<<T as Config>::AccountId>;
752
753 #[pallet::call]
754 impl<T: Config> Pallet<T> {
755 #[pallet::weight(1000)]
757 pub fn f00(_origin: OriginFor<T>) -> DispatchResult {
758 unimplemented!();
759 }
760
761 #[pallet::weight((1000, DispatchClass::Mandatory))]
762 pub fn f01(_origin: OriginFor<T>) -> DispatchResult {
763 unimplemented!();
764 }
765
766 #[pallet::weight((1000, Pays::No))]
767 pub fn f02(_origin: OriginFor<T>) -> DispatchResult {
768 unimplemented!();
769 }
770
771 #[pallet::weight((1000, DispatchClass::Operational, Pays::No))]
772 pub fn f03(_origin: OriginFor<T>) -> DispatchResult {
773 unimplemented!();
774 }
775
776 #[pallet::weight(((_a * 10 + _eb * 1) as u64, DispatchClass::Normal, Pays::Yes))]
778 pub fn f11(_origin: OriginFor<T>, _a: u32, _eb: u32) -> DispatchResult {
779 unimplemented!();
780 }
781
782 #[pallet::weight((0, DispatchClass::Operational, Pays::Yes))]
783 pub fn f12(_origin: OriginFor<T>, _a: u32, _eb: u32) -> DispatchResult {
784 unimplemented!();
785 }
786
787 #[pallet::weight(T::DbWeight::get().reads(3) + T::DbWeight::get().writes(2) + Weight::from_all(10_000))]
788 pub fn f20(_origin: OriginFor<T>) -> DispatchResult {
789 unimplemented!();
790 }
791
792 #[pallet::weight(T::DbWeight::get().reads_writes(6, 5) + Weight::from_all(40_000))]
793 pub fn f21(_origin: OriginFor<T>) -> DispatchResult {
794 unimplemented!();
795 }
796
797 #[pallet::weight(1000)]
798 pub fn f99(_origin: OriginFor<T>) -> DispatchResult {
799 Ok(())
800 }
801
802 #[pallet::weight(1000)]
803 pub fn f100(_origin: OriginFor<T>) -> DispatchResultWithPostInfo {
804 Ok(crate::dispatch::PostDispatchInfo {
805 actual_weight: Some(Weight::from_parts(500, 0)),
806 pays_fee: Pays::Yes,
807 })
808 }
809 }
810
811 pub mod pallet_prelude {
812 pub type OriginFor<T> = <T as super::Config>::RuntimeOrigin;
813
814 pub type HeaderFor<T> =
815 <<T as super::Config>::Block as sp_runtime::traits::HeaderProvider>::HeaderT;
816
817 pub type BlockNumberFor<T> = <HeaderFor<T> as sp_runtime::traits::Header>::Number;
818 }
819 }
820
821 type BlockNumber = u32;
822 type AccountId = u32;
823 type Balance = u32;
824 type Header = generic::Header<BlockNumber, BlakeTwo256>;
825 type UncheckedExtrinsic = generic::UncheckedExtrinsic<u32, RuntimeCall, (), ()>;
826 type Block = generic::Block<Header, UncheckedExtrinsic>;
827
828 crate::construct_runtime!(
829 pub enum Runtime
830 {
831 System: self::frame_system,
832 }
833 );
834
835 parameter_types! {
836 pub const DbWeight: RuntimeDbWeight = RuntimeDbWeight {
837 read: 100,
838 write: 1000,
839 };
840 }
841
842 impl Config for Runtime {
843 type Block = Block;
844 type AccountId = AccountId;
845 type Balance = Balance;
846 type BaseCallFilter = crate::traits::Everything;
847 type RuntimeOrigin = RuntimeOrigin;
848 type RuntimeCall = RuntimeCall;
849 type RuntimeTask = RuntimeTask;
850 type DbWeight = DbWeight;
851 type PalletInfo = PalletInfo;
852 }
853
854 #[test]
855 fn weights_are_correct() {
856 let info = Call::<Runtime>::f00 {}.get_dispatch_info();
858 assert_eq!(info.total_weight(), Weight::from_parts(1000, 0));
859 assert_eq!(info.class, DispatchClass::Normal);
860 assert_eq!(info.pays_fee, Pays::Yes);
861
862 let info = Call::<Runtime>::f01 {}.get_dispatch_info();
864 assert_eq!(info.total_weight(), Weight::from_parts(1000, 0));
865 assert_eq!(info.class, DispatchClass::Mandatory);
866 assert_eq!(info.pays_fee, Pays::Yes);
867
868 let info = Call::<Runtime>::f02 {}.get_dispatch_info();
870 assert_eq!(info.total_weight(), Weight::from_parts(1000, 0));
871 assert_eq!(info.class, DispatchClass::Normal);
872 assert_eq!(info.pays_fee, Pays::No);
873
874 let info = Call::<Runtime>::f03 {}.get_dispatch_info();
876 assert_eq!(info.total_weight(), Weight::from_parts(1000, 0));
877 assert_eq!(info.class, DispatchClass::Operational);
878 assert_eq!(info.pays_fee, Pays::No);
879
880 let info = Call::<Runtime>::f11 { a: 13, eb: 20 }.get_dispatch_info();
882 assert_eq!(info.total_weight(), Weight::from_parts(150, 0)); assert_eq!(info.class, DispatchClass::Normal);
884 assert_eq!(info.pays_fee, Pays::Yes);
885
886 let info = Call::<Runtime>::f12 { a: 10, eb: 20 }.get_dispatch_info();
888 assert_eq!(info.total_weight(), Weight::zero());
889 assert_eq!(info.class, DispatchClass::Operational);
890 assert_eq!(info.pays_fee, Pays::Yes);
891
892 let info = Call::<Runtime>::f20 {}.get_dispatch_info();
895 assert_eq!(info.total_weight(), Weight::from_parts(12300, 10000)); assert_eq!(info.class, DispatchClass::Normal);
897 assert_eq!(info.pays_fee, Pays::Yes);
898
899 let info = Call::<Runtime>::f21 {}.get_dispatch_info();
901 assert_eq!(info.total_weight(), Weight::from_parts(45600, 40000)); assert_eq!(info.class, DispatchClass::Normal);
903 assert_eq!(info.pays_fee, Pays::Yes);
904 }
905
906 #[test]
907 fn extract_actual_weight_works() {
908 let pre = DispatchInfo {
909 call_weight: Weight::from_parts(1000, 0),
910 extension_weight: Weight::zero(),
911 ..Default::default()
912 };
913 assert_eq!(
914 extract_actual_weight(&Ok(from_actual_ref_time(Some(7))), &pre),
915 Weight::from_parts(7, 0)
916 );
917 assert_eq!(
918 extract_actual_weight(&Ok(from_actual_ref_time(Some(1000))), &pre),
919 Weight::from_parts(1000, 0)
920 );
921 assert_eq!(
922 extract_actual_weight(
923 &Err(DispatchError::BadOrigin.with_weight(Weight::from_parts(9, 0))),
924 &pre
925 ),
926 Weight::from_parts(9, 0)
927 );
928 }
929
930 #[test]
931 fn extract_actual_weight_caps_at_pre_weight() {
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(1250))), &pre),
939 Weight::from_parts(1000, 0)
940 );
941 assert_eq!(
942 extract_actual_weight(
943 &Err(DispatchError::BadOrigin.with_weight(Weight::from_parts(1300, 0))),
944 &pre
945 ),
946 Weight::from_parts(1000, 0),
947 );
948 }
949
950 #[test]
951 fn extract_actual_pays_fee_works() {
952 let pre = DispatchInfo {
953 call_weight: Weight::from_parts(1000, 0),
954 extension_weight: Weight::zero(),
955 ..Default::default()
956 };
957 assert_eq!(extract_actual_pays_fee(&Ok(from_actual_ref_time(Some(7))), &pre), Pays::Yes);
958 assert_eq!(
959 extract_actual_pays_fee(&Ok(from_actual_ref_time(Some(1000)).into()), &pre),
960 Pays::Yes
961 );
962 assert_eq!(
963 extract_actual_pays_fee(&Ok(from_post_weight_info(Some(1000), Pays::Yes)), &pre),
964 Pays::Yes
965 );
966 assert_eq!(
967 extract_actual_pays_fee(&Ok(from_post_weight_info(Some(1000), Pays::No)), &pre),
968 Pays::No
969 );
970 assert_eq!(
971 extract_actual_pays_fee(
972 &Err(DispatchError::BadOrigin.with_weight(Weight::from_parts(9, 0))),
973 &pre
974 ),
975 Pays::Yes
976 );
977 assert_eq!(
978 extract_actual_pays_fee(
979 &Err(DispatchErrorWithPostInfo {
980 post_info: PostDispatchInfo { actual_weight: None, pays_fee: Pays::No },
981 error: DispatchError::BadOrigin,
982 }),
983 &pre
984 ),
985 Pays::No
986 );
987
988 let pre = DispatchInfo {
989 call_weight: Weight::from_parts(1000, 0),
990 extension_weight: Weight::zero(),
991 pays_fee: Pays::No,
992 ..Default::default()
993 };
994 assert_eq!(extract_actual_pays_fee(&Ok(from_actual_ref_time(Some(7))), &pre), Pays::No);
995 assert_eq!(extract_actual_pays_fee(&Ok(from_actual_ref_time(Some(1000))), &pre), Pays::No);
996 assert_eq!(
997 extract_actual_pays_fee(&Ok(from_post_weight_info(Some(1000), Pays::Yes)), &pre),
998 Pays::No
999 );
1000 }
1001
1002 #[test]
1003 fn weight_accrue_works() {
1004 let mut post_dispatch = PostDispatchInfo {
1005 actual_weight: Some(Weight::from_parts(1100, 25)),
1006 pays_fee: Pays::Yes,
1007 };
1008 post_dispatch.refund(Weight::from_parts(100, 15));
1009 assert_eq!(
1010 post_dispatch,
1011 PostDispatchInfo {
1012 actual_weight: Some(Weight::from_parts(1000, 10)),
1013 pays_fee: Pays::Yes
1014 }
1015 );
1016
1017 let mut post_dispatch = PostDispatchInfo { actual_weight: None, pays_fee: Pays::Yes };
1018 post_dispatch.refund(Weight::from_parts(100, 15));
1019 assert_eq!(post_dispatch, PostDispatchInfo { actual_weight: None, pays_fee: Pays::Yes });
1020 }
1021}
1022
1023#[cfg(test)]
1024mod per_dispatch_class_tests {
1025 use super::*;
1026 use sp_runtime::traits::Zero;
1027 use DispatchClass::*;
1028
1029 #[test]
1030 fn add_works() {
1031 let a = PerDispatchClass {
1032 normal: (5, 10).into(),
1033 operational: (20, 30).into(),
1034 mandatory: Weight::MAX,
1035 };
1036 assert_eq!(
1037 a.clone()
1038 .add((20, 5).into(), Normal)
1039 .add((10, 10).into(), Operational)
1040 .add((u64::MAX, 3).into(), Mandatory),
1041 PerDispatchClass {
1042 normal: (25, 15).into(),
1043 operational: (30, 40).into(),
1044 mandatory: Weight::MAX
1045 }
1046 );
1047 let b = a
1048 .add(Weight::MAX, Normal)
1049 .add(Weight::MAX, Operational)
1050 .add(Weight::MAX, Mandatory);
1051 assert_eq!(
1052 b,
1053 PerDispatchClass {
1054 normal: Weight::MAX,
1055 operational: Weight::MAX,
1056 mandatory: Weight::MAX
1057 }
1058 );
1059 assert_eq!(b.total(), Weight::MAX);
1060 }
1061
1062 #[test]
1063 fn accrue_works() {
1064 let mut a = PerDispatchClass::default();
1065
1066 a.accrue((10, 15).into(), Normal);
1067 assert_eq!(a.normal, (10, 15).into());
1068 assert_eq!(a.total(), (10, 15).into());
1069
1070 a.accrue((20, 25).into(), Operational);
1071 assert_eq!(a.operational, (20, 25).into());
1072 assert_eq!(a.total(), (30, 40).into());
1073
1074 a.accrue((30, 35).into(), Mandatory);
1075 assert_eq!(a.mandatory, (30, 35).into());
1076 assert_eq!(a.total(), (60, 75).into());
1077
1078 a.accrue((u64::MAX, 10).into(), Operational);
1079 assert_eq!(a.operational, (u64::MAX, 35).into());
1080 assert_eq!(a.total(), (u64::MAX, 85).into());
1081
1082 a.accrue((10, u64::MAX).into(), Normal);
1083 assert_eq!(a.normal, (20, u64::MAX).into());
1084 assert_eq!(a.total(), Weight::MAX);
1085 }
1086
1087 #[test]
1088 fn reduce_works() {
1089 let mut a = PerDispatchClass {
1090 normal: (10, u64::MAX).into(),
1091 mandatory: (u64::MAX, 10).into(),
1092 operational: (20, 20).into(),
1093 };
1094
1095 a.reduce((5, 100).into(), Normal);
1096 assert_eq!(a.normal, (5, u64::MAX - 100).into());
1097 assert_eq!(a.total(), (u64::MAX, u64::MAX - 70).into());
1098
1099 a.reduce((15, 5).into(), Operational);
1100 assert_eq!(a.operational, (5, 15).into());
1101 assert_eq!(a.total(), (u64::MAX, u64::MAX - 75).into());
1102
1103 a.reduce((50, 0).into(), Mandatory);
1104 assert_eq!(a.mandatory, (u64::MAX - 50, 10).into());
1105 assert_eq!(a.total(), (u64::MAX - 40, u64::MAX - 75).into());
1106
1107 a.reduce((u64::MAX, 100).into(), Operational);
1108 assert!(a.operational.is_zero());
1109 assert_eq!(a.total(), (u64::MAX - 45, u64::MAX - 90).into());
1110
1111 a.reduce((5, u64::MAX).into(), Normal);
1112 assert!(a.normal.is_zero());
1113 assert_eq!(a.total(), (u64::MAX - 50, 10).into());
1114 }
1115
1116 #[test]
1117 fn checked_accrue_works() {
1118 let mut a = PerDispatchClass::default();
1119
1120 a.checked_accrue((1, 2).into(), Normal).unwrap();
1121 a.checked_accrue((3, 4).into(), Operational).unwrap();
1122 a.checked_accrue((5, 6).into(), Mandatory).unwrap();
1123 a.checked_accrue((7, 8).into(), Operational).unwrap();
1124 a.checked_accrue((9, 0).into(), Normal).unwrap();
1125
1126 assert_eq!(
1127 a,
1128 PerDispatchClass {
1129 normal: (10, 2).into(),
1130 operational: (10, 12).into(),
1131 mandatory: (5, 6).into(),
1132 }
1133 );
1134
1135 a.checked_accrue((u64::MAX - 10, u64::MAX - 2).into(), Normal).unwrap();
1136 a.checked_accrue((0, 0).into(), Normal).unwrap();
1137 a.checked_accrue((1, 0).into(), Normal).unwrap_err();
1138 a.checked_accrue((0, 1).into(), Normal).unwrap_err();
1139
1140 assert_eq!(
1141 a,
1142 PerDispatchClass {
1143 normal: Weight::MAX,
1144 operational: (10, 12).into(),
1145 mandatory: (5, 6).into(),
1146 }
1147 );
1148 }
1149
1150 #[test]
1151 fn checked_accrue_does_not_modify_on_error() {
1152 let mut a = PerDispatchClass {
1153 normal: 0.into(),
1154 operational: Weight::MAX / 2 + 2.into(),
1155 mandatory: 10.into(),
1156 };
1157
1158 a.checked_accrue(Weight::MAX / 2, Operational).unwrap_err();
1159 a.checked_accrue(Weight::MAX - 9.into(), Mandatory).unwrap_err();
1160 a.checked_accrue(Weight::MAX, Normal).unwrap(); assert_eq!(
1163 a,
1164 PerDispatchClass {
1165 normal: Weight::MAX,
1166 operational: Weight::MAX / 2 + 2.into(),
1167 mandatory: 10.into(),
1168 }
1169 );
1170 }
1171
1172 #[test]
1173 fn total_works() {
1174 assert!(PerDispatchClass::default().total().is_zero());
1175
1176 assert_eq!(
1177 PerDispatchClass {
1178 normal: 0.into(),
1179 operational: (10, 20).into(),
1180 mandatory: (20, u64::MAX).into(),
1181 }
1182 .total(),
1183 (30, u64::MAX).into()
1184 );
1185
1186 assert_eq!(
1187 PerDispatchClass {
1188 normal: (u64::MAX - 10, 10).into(),
1189 operational: (3, u64::MAX).into(),
1190 mandatory: (4, u64::MAX).into(),
1191 }
1192 .total(),
1193 (u64::MAX - 3, u64::MAX).into()
1194 );
1195 }
1196}
1197
1198#[cfg(test)]
1199mod test_extensions {
1200 use codec::{Decode, DecodeWithMemTracking, Encode};
1201 use scale_info::TypeInfo;
1202 use sp_runtime::{
1203 impl_tx_ext_default,
1204 traits::{
1205 DispatchInfoOf, DispatchOriginOf, Dispatchable, PostDispatchInfoOf,
1206 TransactionExtension,
1207 },
1208 transaction_validity::TransactionValidityError,
1209 };
1210 use sp_weights::Weight;
1211
1212 use super::{DispatchResult, PostDispatchInfo};
1213
1214 #[derive(Clone, Eq, PartialEq, Debug, Encode, Decode, DecodeWithMemTracking, TypeInfo)]
1216 pub struct HalfCostIf(pub bool);
1217
1218 impl<RuntimeCall: Dispatchable> TransactionExtension<RuntimeCall> for HalfCostIf {
1219 const IDENTIFIER: &'static str = "HalfCostIf";
1220 type Implicit = ();
1221 type Val = ();
1222 type Pre = bool;
1223
1224 fn weight(&self, _: &RuntimeCall) -> sp_weights::Weight {
1225 Weight::from_parts(100, 0)
1226 }
1227
1228 fn prepare(
1229 self,
1230 _val: Self::Val,
1231 _origin: &DispatchOriginOf<RuntimeCall>,
1232 _call: &RuntimeCall,
1233 _info: &DispatchInfoOf<RuntimeCall>,
1234 _len: usize,
1235 ) -> Result<Self::Pre, TransactionValidityError> {
1236 Ok(self.0)
1237 }
1238
1239 fn post_dispatch_details(
1240 pre: Self::Pre,
1241 _info: &DispatchInfoOf<RuntimeCall>,
1242 _post_info: &PostDispatchInfoOf<RuntimeCall>,
1243 _len: usize,
1244 _result: &DispatchResult,
1245 ) -> Result<Weight, TransactionValidityError> {
1246 if pre {
1247 Ok(Weight::from_parts(50, 0))
1248 } else {
1249 Ok(Weight::zero())
1250 }
1251 }
1252 impl_tx_ext_default!(RuntimeCall; validate);
1253 }
1254
1255 #[derive(Clone, Eq, PartialEq, Debug, Encode, Decode, DecodeWithMemTracking, TypeInfo)]
1258 pub struct FreeIfUnder(pub u64);
1259
1260 impl<RuntimeCall: Dispatchable> TransactionExtension<RuntimeCall> for FreeIfUnder
1261 where
1262 RuntimeCall: Dispatchable<PostInfo = PostDispatchInfo>,
1263 {
1264 const IDENTIFIER: &'static str = "FreeIfUnder";
1265 type Implicit = ();
1266 type Val = ();
1267 type Pre = u64;
1268
1269 fn weight(&self, _: &RuntimeCall) -> sp_weights::Weight {
1270 Weight::from_parts(200, 0)
1271 }
1272
1273 fn prepare(
1274 self,
1275 _val: Self::Val,
1276 _origin: &DispatchOriginOf<RuntimeCall>,
1277 _call: &RuntimeCall,
1278 _info: &DispatchInfoOf<RuntimeCall>,
1279 _len: usize,
1280 ) -> Result<Self::Pre, TransactionValidityError> {
1281 Ok(self.0)
1282 }
1283
1284 fn post_dispatch_details(
1285 pre: Self::Pre,
1286 _info: &DispatchInfoOf<RuntimeCall>,
1287 post_info: &PostDispatchInfoOf<RuntimeCall>,
1288 _len: usize,
1289 _result: &DispatchResult,
1290 ) -> Result<Weight, TransactionValidityError> {
1291 if let Some(actual) = post_info.actual_weight {
1292 if pre > actual.ref_time() {
1293 return Ok(Weight::from_parts(200, 0));
1294 }
1295 }
1296 Ok(Weight::zero())
1297 }
1298 impl_tx_ext_default!(RuntimeCall; validate);
1299 }
1300
1301 #[derive(Clone, Eq, PartialEq, Debug, Encode, Decode, DecodeWithMemTracking, TypeInfo)]
1304 pub struct ActualWeightIs(pub u64);
1305
1306 impl<RuntimeCall: Dispatchable> TransactionExtension<RuntimeCall> for ActualWeightIs {
1307 const IDENTIFIER: &'static str = "ActualWeightIs";
1308 type Implicit = ();
1309 type Val = ();
1310 type Pre = u64;
1311
1312 fn weight(&self, _: &RuntimeCall) -> sp_weights::Weight {
1313 Weight::from_parts(300, 0)
1314 }
1315
1316 fn prepare(
1317 self,
1318 _val: Self::Val,
1319 _origin: &DispatchOriginOf<RuntimeCall>,
1320 _call: &RuntimeCall,
1321 _info: &DispatchInfoOf<RuntimeCall>,
1322 _len: usize,
1323 ) -> Result<Self::Pre, TransactionValidityError> {
1324 Ok(self.0)
1325 }
1326
1327 fn post_dispatch_details(
1328 pre: Self::Pre,
1329 _info: &DispatchInfoOf<RuntimeCall>,
1330 _post_info: &PostDispatchInfoOf<RuntimeCall>,
1331 _len: usize,
1332 _result: &DispatchResult,
1333 ) -> Result<Weight, TransactionValidityError> {
1334 Ok(Weight::from_parts(300u64.saturating_sub(pre), 0))
1335 }
1336 impl_tx_ext_default!(RuntimeCall; validate);
1337 }
1338}
1339
1340#[cfg(test)]
1341#[allow(dead_code)]
1343mod extension_weight_tests {
1344 use crate::assert_ok;
1345
1346 use super::*;
1347 use sp_core::parameter_types;
1348 use sp_runtime::{
1349 generic::{self, ExtrinsicFormat},
1350 traits::{Applyable, BlakeTwo256, DispatchTransaction, TransactionExtension},
1351 };
1352 use sp_weights::RuntimeDbWeight;
1353 use test_extensions::{ActualWeightIs, FreeIfUnder, HalfCostIf};
1354
1355 use super::weight_tests::frame_system;
1356 use frame_support::construct_runtime;
1357
1358 pub type TxExtension = (HalfCostIf, FreeIfUnder, ActualWeightIs);
1359 pub type UncheckedExtrinsic = generic::UncheckedExtrinsic<u64, RuntimeCall, (), TxExtension>;
1360 pub type Header = generic::Header<BlockNumber, BlakeTwo256>;
1361 pub type Block = generic::Block<Header, UncheckedExtrinsic>;
1362 pub type AccountId = u64;
1363 pub type Balance = u32;
1364 pub type BlockNumber = u32;
1365
1366 construct_runtime!(
1367 pub enum ExtRuntime {
1368 System: frame_system,
1369 }
1370 );
1371
1372 impl frame_system::Config for ExtRuntime {
1373 type Block = Block;
1374 type AccountId = AccountId;
1375 type Balance = Balance;
1376 type BaseCallFilter = crate::traits::Everything;
1377 type RuntimeOrigin = RuntimeOrigin;
1378 type RuntimeCall = RuntimeCall;
1379 type RuntimeTask = RuntimeTask;
1380 type DbWeight = DbWeight;
1381 type PalletInfo = PalletInfo;
1382 }
1383
1384 parameter_types! {
1385 pub const DbWeight: RuntimeDbWeight = RuntimeDbWeight {
1386 read: 100,
1387 write: 1000,
1388 };
1389 }
1390
1391 pub struct ExtBuilder {}
1392
1393 impl Default for ExtBuilder {
1394 fn default() -> Self {
1395 Self {}
1396 }
1397 }
1398
1399 impl ExtBuilder {
1400 pub fn build(self) -> sp_io::TestExternalities {
1401 let mut ext = sp_io::TestExternalities::new(Default::default());
1402 ext.execute_with(|| {});
1403 ext
1404 }
1405
1406 pub fn build_and_execute(self, test: impl FnOnce() -> ()) {
1407 self.build().execute_with(|| {
1408 test();
1409 })
1410 }
1411 }
1412
1413 #[test]
1414 fn no_post_dispatch_with_no_refund() {
1415 ExtBuilder::default().build_and_execute(|| {
1416 let call = RuntimeCall::System(frame_system::Call::<ExtRuntime>::f99 {});
1417 let ext: TxExtension = (HalfCostIf(false), FreeIfUnder(1500), ActualWeightIs(0));
1418 let uxt = UncheckedExtrinsic::new_signed(call.clone(), 0, (), ext.clone());
1419 assert_eq!(uxt.extension_weight(), Weight::from_parts(600, 0));
1420
1421 let mut info = call.get_dispatch_info();
1422 assert_eq!(info.total_weight(), Weight::from_parts(1000, 0));
1423 info.extension_weight = ext.weight(&call);
1424 let (pre, _) = ext.validate_and_prepare(Some(0).into(), &call, &info, 0, 0).unwrap();
1425 let res = call.dispatch(Some(0).into());
1426 let mut post_info = res.unwrap();
1427 assert!(post_info.actual_weight.is_none());
1428 assert_ok!(<TxExtension as TransactionExtension<RuntimeCall>>::post_dispatch(
1429 pre,
1430 &info,
1431 &mut post_info,
1432 0,
1433 &Ok(()),
1434 ));
1435 assert!(post_info.actual_weight.is_none());
1436 });
1437 }
1438
1439 #[test]
1440 fn no_post_dispatch_refunds_when_dispatched() {
1441 ExtBuilder::default().build_and_execute(|| {
1442 let call = RuntimeCall::System(frame_system::Call::<ExtRuntime>::f99 {});
1443 let ext: TxExtension = (HalfCostIf(true), FreeIfUnder(100), ActualWeightIs(0));
1444 let uxt = UncheckedExtrinsic::new_signed(call.clone(), 0, (), ext.clone());
1445 assert_eq!(uxt.extension_weight(), Weight::from_parts(600, 0));
1446
1447 let mut info = call.get_dispatch_info();
1448 assert_eq!(info.total_weight(), Weight::from_parts(1000, 0));
1449 info.extension_weight = ext.weight(&call);
1450 let post_info =
1451 ext.dispatch_transaction(Some(0).into(), call, &info, 0, 0).unwrap().unwrap();
1452 assert_eq!(post_info.actual_weight, Some(Weight::from_parts(1250, 0)));
1454 });
1455 }
1456
1457 #[test]
1458 fn post_dispatch_with_refunds() {
1459 ExtBuilder::default().build_and_execute(|| {
1460 let call = RuntimeCall::System(frame_system::Call::<ExtRuntime>::f100 {});
1461 let ext: TxExtension = (HalfCostIf(false), FreeIfUnder(2000), ActualWeightIs(0));
1463 let uxt = UncheckedExtrinsic::new_signed(call.clone(), 0, (), ext.clone());
1464 assert_eq!(uxt.extension_weight(), Weight::from_parts(600, 0));
1465
1466 let mut info = call.get_dispatch_info();
1467 assert_eq!(info.call_weight, Weight::from_parts(1000, 0));
1468 info.extension_weight = ext.weight(&call);
1469 assert_eq!(info.total_weight(), Weight::from_parts(1600, 0));
1470 let (pre, _) = ext.validate_and_prepare(Some(0).into(), &call, &info, 0, 0).unwrap();
1471 let res = call.clone().dispatch(Some(0).into());
1472 let mut post_info = res.unwrap();
1473 assert_eq!(post_info.actual_weight, Some(Weight::from_parts(500, 0)));
1475 post_info.set_extension_weight(&info);
1477 assert_ok!(<TxExtension as TransactionExtension<RuntimeCall>>::post_dispatch(
1479 pre,
1480 &info,
1481 &mut post_info,
1482 0,
1483 &Ok(()),
1484 ));
1485 assert_eq!(post_info.actual_weight, Some(Weight::from_parts(600, 0)));
1487
1488 let ext: TxExtension = (HalfCostIf(false), FreeIfUnder(1100), ActualWeightIs(200));
1490 let (pre, _) = ext.validate_and_prepare(Some(0).into(), &call, &info, 0, 0).unwrap();
1491 let res = call.clone().dispatch(Some(0).into());
1492 let mut post_info = res.unwrap();
1493 assert_eq!(post_info.actual_weight, Some(Weight::from_parts(500, 0)));
1495 post_info.set_extension_weight(&info);
1497 assert_ok!(<TxExtension as TransactionExtension<RuntimeCall>>::post_dispatch(
1499 pre,
1500 &info,
1501 &mut post_info,
1502 0,
1503 &Ok(()),
1504 ));
1505 assert_eq!(post_info.actual_weight, Some(Weight::from_parts(1000, 0)));
1507
1508 let ext: TxExtension = (HalfCostIf(true), FreeIfUnder(1060), ActualWeightIs(200));
1510 let (pre, _) = ext.validate_and_prepare(Some(0).into(), &call, &info, 0, 0).unwrap();
1511 let res = call.clone().dispatch(Some(0).into());
1512 let mut post_info = res.unwrap();
1513 assert_eq!(post_info.actual_weight, Some(Weight::from_parts(500, 0)));
1515 post_info.set_extension_weight(&info);
1517 assert_ok!(<TxExtension as TransactionExtension<RuntimeCall>>::post_dispatch(
1519 pre,
1520 &info,
1521 &mut post_info,
1522 0,
1523 &Ok(()),
1524 ));
1525 assert_eq!(post_info.actual_weight, Some(Weight::from_parts(750, 0)));
1527
1528 let ext: TxExtension = (HalfCostIf(false), FreeIfUnder(100), ActualWeightIs(300));
1530 let (pre, _) = ext.validate_and_prepare(Some(0).into(), &call, &info, 0, 0).unwrap();
1531 let res = call.clone().dispatch(Some(0).into());
1532 let mut post_info = res.unwrap();
1533 assert_eq!(post_info.actual_weight, Some(Weight::from_parts(500, 0)));
1535 post_info.set_extension_weight(&info);
1537 assert_ok!(<TxExtension as TransactionExtension<RuntimeCall>>::post_dispatch(
1539 pre,
1540 &info,
1541 &mut post_info,
1542 0,
1543 &Ok(()),
1544 ));
1545 assert_eq!(post_info.actual_weight, Some(Weight::from_parts(1100, 0)));
1547 });
1548 }
1549
1550 #[test]
1551 fn checked_extrinsic_apply() {
1552 ExtBuilder::default().build_and_execute(|| {
1553 let call = RuntimeCall::System(frame_system::Call::<ExtRuntime>::f100 {});
1554 let ext: TxExtension = (HalfCostIf(false), FreeIfUnder(2000), ActualWeightIs(0));
1556 let xt = CheckedExtrinsic {
1557 format: ExtrinsicFormat::<_, _>::Signed(0, ext.clone()),
1558 function: call.clone(),
1559 };
1560 assert_eq!(xt.extension_weight(), Weight::from_parts(600, 0));
1561 let mut info = call.get_dispatch_info();
1562 assert_eq!(info.call_weight, Weight::from_parts(1000, 0));
1563 info.extension_weight = ext.weight(&call);
1564 assert_eq!(info.total_weight(), Weight::from_parts(1600, 0));
1565 let post_info = xt.apply::<ExtRuntime>(&info, 0).unwrap().unwrap();
1566 assert_eq!(post_info.actual_weight, Some(Weight::from_parts(600, 0)));
1568
1569 let ext: TxExtension = (HalfCostIf(false), FreeIfUnder(1100), ActualWeightIs(200));
1571 let xt = CheckedExtrinsic {
1572 format: ExtrinsicFormat::<_, _>::Signed(0, ext),
1573 function: call.clone(),
1574 };
1575 let post_info = xt.apply::<ExtRuntime>(&info, 0).unwrap().unwrap();
1576 assert_eq!(post_info.actual_weight, Some(Weight::from_parts(1000, 0)));
1578
1579 let ext: TxExtension = (HalfCostIf(true), FreeIfUnder(1060), ActualWeightIs(200));
1581 let xt = CheckedExtrinsic {
1582 format: ExtrinsicFormat::<_, _>::Signed(0, ext),
1583 function: call.clone(),
1584 };
1585 let post_info = xt.apply::<ExtRuntime>(&info, 0).unwrap().unwrap();
1586 assert_eq!(post_info.actual_weight, Some(Weight::from_parts(750, 0)));
1588
1589 let ext: TxExtension = (HalfCostIf(false), FreeIfUnder(100), ActualWeightIs(300));
1591 let xt = CheckedExtrinsic {
1592 format: ExtrinsicFormat::<_, _>::Signed(0, ext),
1593 function: call.clone(),
1594 };
1595 let post_info = xt.apply::<ExtRuntime>(&info, 0).unwrap().unwrap();
1596 assert_eq!(post_info.actual_weight, Some(Weight::from_parts(1100, 0)));
1598 });
1599 }
1600}