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