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