1use crate::traits::UnfilteredDispatchable;
22use codec::{Codec, Decode, 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::SignedExtension,
30 DispatchError, RuntimeDebug,
31};
32use sp_weights::Weight;
33
34pub type DispatchResultWithPostInfo = sp_runtime::DispatchResultWithInfo<PostDispatchInfo>;
38
39#[docify::export]
40pub type DispatchResult = Result<(), sp_runtime::DispatchError>;
45
46pub type DispatchErrorWithPostInfo = sp_runtime::DispatchErrorWithPostInfo<PostDispatchInfo>;
48
49pub trait Callable<T> {
51 type RuntimeCall: UnfilteredDispatchable + Codec + Clone + PartialEq + Eq;
52}
53
54pub type CallableCallFor<A, R> = <A as Callable<R>>::RuntimeCall;
57
58pub trait CheckIfFeeless {
64 type Origin;
66
67 fn is_feeless(&self, origin: &Self::Origin) -> bool;
70}
71
72#[derive(PartialEq, Eq, Clone, RuntimeDebug, Encode, Decode, TypeInfo, MaxEncodedLen)]
74pub enum RawOrigin<AccountId> {
75 Root,
77 Signed(AccountId),
79 None,
83}
84
85impl<AccountId> From<Option<AccountId>> for RawOrigin<AccountId> {
86 fn from(s: Option<AccountId>) -> RawOrigin<AccountId> {
87 match s {
88 Some(who) => RawOrigin::Signed(who),
89 None => RawOrigin::None,
90 }
91 }
92}
93
94impl<AccountId> RawOrigin<AccountId> {
95 pub fn as_signed(&self) -> Option<&AccountId> {
97 match &self {
98 Self::Signed(x) => Some(x),
99 _ => None,
100 }
101 }
102
103 pub fn is_root(&self) -> bool {
105 matches!(&self, Self::Root)
106 }
107
108 pub fn is_none(&self) -> bool {
110 matches!(&self, Self::None)
111 }
112}
113
114pub trait Parameter: Codec + EncodeLike + Clone + Eq + fmt::Debug + scale_info::TypeInfo {}
118impl<T> Parameter for T where T: Codec + EncodeLike + Clone + Eq + fmt::Debug + scale_info::TypeInfo {}
119
120pub trait ClassifyDispatch<T> {
122 fn classify_dispatch(&self, target: T) -> DispatchClass;
126}
127
128pub trait PaysFee<T> {
132 fn pays_fee(&self, _target: T) -> Pays;
133}
134
135#[derive(Clone, Copy, Eq, PartialEq, RuntimeDebug, Encode, Decode, TypeInfo)]
137pub enum Pays {
138 Yes,
140 No,
142}
143
144impl Default for Pays {
145 fn default() -> Self {
146 Self::Yes
147 }
148}
149
150impl From<Pays> for PostDispatchInfo {
151 fn from(pays_fee: Pays) -> Self {
152 Self { actual_weight: None, pays_fee }
153 }
154}
155
156impl From<bool> for Pays {
157 fn from(b: bool) -> Self {
158 match b {
159 true => Self::Yes,
160 false => Self::No,
161 }
162 }
163}
164
165#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
170#[cfg_attr(feature = "std", serde(rename_all = "camelCase"))]
171#[derive(PartialEq, Eq, Clone, Copy, Encode, Decode, RuntimeDebug, TypeInfo)]
172pub enum DispatchClass {
173 Normal,
175 Operational,
177 Mandatory,
191}
192
193impl Default for DispatchClass {
194 fn default() -> Self {
195 Self::Normal
196 }
197}
198
199impl DispatchClass {
200 pub fn all() -> &'static [DispatchClass] {
202 &[DispatchClass::Normal, DispatchClass::Operational, DispatchClass::Mandatory]
203 }
204
205 pub fn non_mandatory() -> &'static [DispatchClass] {
207 &[DispatchClass::Normal, DispatchClass::Operational]
208 }
209}
210
211pub trait OneOrMany<T> {
216 type Iter: Iterator<Item = T>;
218 fn into_iter(self) -> Self::Iter;
220}
221
222impl OneOrMany<DispatchClass> for DispatchClass {
223 type Iter = core::iter::Once<DispatchClass>;
224 fn into_iter(self) -> Self::Iter {
225 core::iter::once(self)
226 }
227}
228
229impl<'a> OneOrMany<DispatchClass> for &'a [DispatchClass] {
230 type Iter = core::iter::Cloned<core::slice::Iter<'a, DispatchClass>>;
231 fn into_iter(self) -> Self::Iter {
232 self.iter().cloned()
233 }
234}
235
236#[derive(Clone, Copy, Eq, PartialEq, Default, RuntimeDebug, Encode, Decode, TypeInfo)]
238pub struct DispatchInfo {
239 pub weight: Weight,
241 pub class: DispatchClass,
243 pub pays_fee: Pays,
245}
246
247pub trait GetDispatchInfo {
250 fn get_dispatch_info(&self) -> DispatchInfo;
254}
255
256impl GetDispatchInfo for () {
257 fn get_dispatch_info(&self) -> DispatchInfo {
258 DispatchInfo::default()
259 }
260}
261
262pub fn extract_actual_weight(result: &DispatchResultWithPostInfo, info: &DispatchInfo) -> Weight {
264 match result {
265 Ok(post_info) => post_info,
266 Err(err) => &err.post_info,
267 }
268 .calc_actual_weight(info)
269}
270
271pub fn extract_actual_pays_fee(result: &DispatchResultWithPostInfo, info: &DispatchInfo) -> Pays {
273 match result {
274 Ok(post_info) => post_info,
275 Err(err) => &err.post_info,
276 }
277 .pays_fee(info)
278}
279
280#[derive(Clone, Copy, Eq, PartialEq, Default, RuntimeDebug, Encode, Decode, TypeInfo)]
283pub struct PostDispatchInfo {
284 pub actual_weight: Option<Weight>,
286 pub pays_fee: Pays,
288}
289
290impl PostDispatchInfo {
291 pub fn calc_unspent(&self, info: &DispatchInfo) -> Weight {
293 info.weight - self.calc_actual_weight(info)
294 }
295
296 pub fn calc_actual_weight(&self, info: &DispatchInfo) -> Weight {
298 if let Some(actual_weight) = self.actual_weight {
299 actual_weight.min(info.weight)
300 } else {
301 info.weight
302 }
303 }
304
305 pub fn pays_fee(&self, info: &DispatchInfo) -> Pays {
307 if info.pays_fee == Pays::No || self.pays_fee == Pays::No {
312 Pays::No
313 } else {
314 Pays::Yes
316 }
317 }
318}
319
320impl From<()> for PostDispatchInfo {
321 fn from(_: ()) -> Self {
322 Self { actual_weight: None, pays_fee: Default::default() }
323 }
324}
325
326impl sp_runtime::traits::Printable for PostDispatchInfo {
327 fn print(&self) {
328 "actual_weight=".print();
329 match self.actual_weight {
330 Some(weight) => weight.print(),
331 None => "max-weight".print(),
332 };
333 "pays_fee=".print();
334 match self.pays_fee {
335 Pays::Yes => "Yes".print(),
336 Pays::No => "No".print(),
337 }
338 }
339}
340
341pub trait WithPostDispatchInfo {
344 fn with_weight(self, actual_weight: Weight) -> DispatchErrorWithPostInfo;
353}
354
355impl<T> WithPostDispatchInfo for T
356where
357 T: Into<DispatchError>,
358{
359 fn with_weight(self, actual_weight: Weight) -> DispatchErrorWithPostInfo {
360 DispatchErrorWithPostInfo {
361 post_info: PostDispatchInfo {
362 actual_weight: Some(actual_weight),
363 pays_fee: Default::default(),
364 },
365 error: self.into(),
366 }
367 }
368}
369
370impl<Address, Call, Signature, Extra> GetDispatchInfo
372 for UncheckedExtrinsic<Address, Call, Signature, Extra>
373where
374 Call: GetDispatchInfo,
375 Extra: SignedExtension,
376{
377 fn get_dispatch_info(&self) -> DispatchInfo {
378 self.function.get_dispatch_info()
379 }
380}
381
382impl<AccountId, Call, Extra> GetDispatchInfo for CheckedExtrinsic<AccountId, Call, Extra>
384where
385 Call: GetDispatchInfo,
386{
387 fn get_dispatch_info(&self) -> DispatchInfo {
388 self.function.get_dispatch_info()
389 }
390}
391
392#[cfg(feature = "std")]
394impl<Call: Encode + GetDispatchInfo, Extra: Encode> GetDispatchInfo
395 for sp_runtime::testing::TestXt<Call, Extra>
396{
397 fn get_dispatch_info(&self) -> DispatchInfo {
398 DispatchInfo {
400 weight: Weight::from_parts(self.encode().len() as _, 0),
401 pays_fee: Pays::Yes,
402 class: self.call.get_dispatch_info().class,
403 }
404 }
405}
406
407#[derive(Clone, Eq, PartialEq, Default, Debug, Encode, Decode, TypeInfo, MaxEncodedLen)]
409pub struct PerDispatchClass<T> {
410 normal: T,
412 operational: T,
414 mandatory: T,
416}
417
418impl<T> PerDispatchClass<T> {
419 pub fn new(val: impl Fn(DispatchClass) -> T) -> Self {
421 Self {
422 normal: val(DispatchClass::Normal),
423 operational: val(DispatchClass::Operational),
424 mandatory: val(DispatchClass::Mandatory),
425 }
426 }
427
428 pub fn get_mut(&mut self, class: DispatchClass) -> &mut T {
430 match class {
431 DispatchClass::Operational => &mut self.operational,
432 DispatchClass::Normal => &mut self.normal,
433 DispatchClass::Mandatory => &mut self.mandatory,
434 }
435 }
436
437 pub fn get(&self, class: DispatchClass) -> &T {
439 match class {
440 DispatchClass::Normal => &self.normal,
441 DispatchClass::Operational => &self.operational,
442 DispatchClass::Mandatory => &self.mandatory,
443 }
444 }
445}
446
447impl<T: Clone> PerDispatchClass<T> {
448 pub fn set(&mut self, new: T, class: impl OneOrMany<DispatchClass>) {
450 for class in class.into_iter() {
451 *self.get_mut(class) = new.clone();
452 }
453 }
454}
455
456impl PerDispatchClass<Weight> {
457 pub fn total(&self) -> Weight {
461 let mut sum = Weight::zero();
462 for class in DispatchClass::all() {
463 sum.saturating_accrue(*self.get(*class));
464 }
465 sum
466 }
467
468 pub fn add(mut self, weight: Weight, class: DispatchClass) -> Self {
470 self.accrue(weight, class);
471 self
472 }
473
474 pub fn accrue(&mut self, weight: Weight, class: DispatchClass) {
476 self.get_mut(class).saturating_accrue(weight);
477 }
478
479 pub fn checked_accrue(&mut self, weight: Weight, class: DispatchClass) -> Result<(), ()> {
481 self.get_mut(class).checked_accrue(weight).ok_or(())
482 }
483
484 pub fn reduce(&mut self, weight: Weight, class: DispatchClass) {
486 self.get_mut(class).saturating_reduce(weight);
487 }
488}
489
490pub trait WeighData<T> {
492 fn weigh_data(&self, target: T) -> Weight;
495}
496
497impl<T> WeighData<T> for Weight {
498 fn weigh_data(&self, _: T) -> Weight {
499 return *self
500 }
501}
502
503impl<T> PaysFee<T> for (Weight, DispatchClass, Pays) {
504 fn pays_fee(&self, _: T) -> Pays {
505 self.2
506 }
507}
508
509impl<T> WeighData<T> for (Weight, DispatchClass) {
510 fn weigh_data(&self, args: T) -> Weight {
511 return self.0.weigh_data(args)
512 }
513}
514
515impl<T> WeighData<T> for (Weight, DispatchClass, Pays) {
516 fn weigh_data(&self, args: T) -> Weight {
517 return self.0.weigh_data(args)
518 }
519}
520
521impl<T> ClassifyDispatch<T> for (Weight, DispatchClass) {
522 fn classify_dispatch(&self, _: T) -> DispatchClass {
523 self.1
524 }
525}
526
527impl<T> PaysFee<T> for (Weight, DispatchClass) {
528 fn pays_fee(&self, _: T) -> Pays {
529 Pays::Yes
530 }
531}
532
533impl<T> WeighData<T> for (Weight, Pays) {
534 fn weigh_data(&self, args: T) -> Weight {
535 return self.0.weigh_data(args)
536 }
537}
538
539impl<T> ClassifyDispatch<T> for (Weight, Pays) {
540 fn classify_dispatch(&self, _: T) -> DispatchClass {
541 DispatchClass::Normal
542 }
543}
544
545impl<T> PaysFee<T> for (Weight, Pays) {
546 fn pays_fee(&self, _: T) -> Pays {
547 self.1
548 }
549}
550
551impl From<(Option<Weight>, Pays)> for PostDispatchInfo {
552 fn from(post_weight_info: (Option<Weight>, Pays)) -> Self {
553 let (actual_weight, pays_fee) = post_weight_info;
554 Self { actual_weight, pays_fee }
555 }
556}
557
558impl From<Option<Weight>> for PostDispatchInfo {
559 fn from(actual_weight: Option<Weight>) -> Self {
560 Self { actual_weight, pays_fee: Default::default() }
561 }
562}
563
564impl<T> ClassifyDispatch<T> for Weight {
565 fn classify_dispatch(&self, _: T) -> DispatchClass {
566 DispatchClass::Normal
567 }
568}
569
570impl<T> PaysFee<T> for Weight {
571 fn pays_fee(&self, _: T) -> Pays {
572 Pays::Yes
573 }
574}
575
576impl<T> ClassifyDispatch<T> for (Weight, DispatchClass, Pays) {
577 fn classify_dispatch(&self, _: T) -> DispatchClass {
578 self.1
579 }
580}
581
582impl<T> ClassifyDispatch<T> for u64 {
585 fn classify_dispatch(&self, _: T) -> DispatchClass {
586 DispatchClass::Normal
587 }
588}
589
590impl<T> PaysFee<T> for u64 {
591 fn pays_fee(&self, _: T) -> Pays {
592 Pays::Yes
593 }
594}
595
596impl<T> WeighData<T> for u64 {
597 fn weigh_data(&self, _: T) -> Weight {
598 return Weight::from_parts(*self, 0)
599 }
600}
601
602impl<T> WeighData<T> for (u64, DispatchClass, Pays) {
603 fn weigh_data(&self, args: T) -> Weight {
604 return self.0.weigh_data(args)
605 }
606}
607
608impl<T> ClassifyDispatch<T> for (u64, DispatchClass, Pays) {
609 fn classify_dispatch(&self, _: T) -> DispatchClass {
610 self.1
611 }
612}
613
614impl<T> PaysFee<T> for (u64, DispatchClass, Pays) {
615 fn pays_fee(&self, _: T) -> Pays {
616 self.2
617 }
618}
619
620impl<T> WeighData<T> for (u64, DispatchClass) {
621 fn weigh_data(&self, args: T) -> Weight {
622 return self.0.weigh_data(args)
623 }
624}
625
626impl<T> ClassifyDispatch<T> for (u64, DispatchClass) {
627 fn classify_dispatch(&self, _: T) -> DispatchClass {
628 self.1
629 }
630}
631
632impl<T> PaysFee<T> for (u64, DispatchClass) {
633 fn pays_fee(&self, _: T) -> Pays {
634 Pays::Yes
635 }
636}
637
638impl<T> WeighData<T> for (u64, Pays) {
639 fn weigh_data(&self, args: T) -> Weight {
640 return self.0.weigh_data(args)
641 }
642}
643
644impl<T> ClassifyDispatch<T> for (u64, Pays) {
645 fn classify_dispatch(&self, _: T) -> DispatchClass {
646 DispatchClass::Normal
647 }
648}
649
650impl<T> PaysFee<T> for (u64, Pays) {
651 fn pays_fee(&self, _: T) -> Pays {
652 self.1
653 }
654}
655
656#[cfg(test)]
659#[allow(dead_code)]
661mod weight_tests {
662 use super::*;
663 use sp_core::parameter_types;
664 use sp_runtime::{generic, traits::BlakeTwo256};
665 use sp_weights::RuntimeDbWeight;
666
667 pub use self::frame_system::{Call, Config};
668
669 fn from_actual_ref_time(ref_time: Option<u64>) -> PostDispatchInfo {
670 PostDispatchInfo {
671 actual_weight: ref_time.map(|t| Weight::from_all(t)),
672 pays_fee: Default::default(),
673 }
674 }
675
676 fn from_post_weight_info(ref_time: Option<u64>, pays_fee: Pays) -> PostDispatchInfo {
677 PostDispatchInfo { actual_weight: ref_time.map(|t| Weight::from_all(t)), pays_fee }
678 }
679
680 #[crate::pallet(dev_mode)]
681 pub mod frame_system {
682 use super::{frame_system, frame_system::pallet_prelude::*};
683 pub use crate::dispatch::RawOrigin;
684 use crate::pallet_prelude::*;
685
686 #[pallet::pallet]
687 pub struct Pallet<T>(_);
688
689 #[pallet::config]
690 #[pallet::disable_frame_system_supertrait_check]
691 pub trait Config: 'static {
692 type Block: Parameter + sp_runtime::traits::Block;
693 type AccountId;
694 type Balance;
695 type BaseCallFilter: crate::traits::Contains<Self::RuntimeCall>;
696 type RuntimeOrigin;
697 type RuntimeCall;
698 type RuntimeTask;
699 type PalletInfo: crate::traits::PalletInfo;
700 type DbWeight: Get<crate::weights::RuntimeDbWeight>;
701 }
702
703 #[pallet::error]
704 pub enum Error<T> {
705 CallFiltered,
707 }
708
709 #[pallet::origin]
710 pub type Origin<T> = RawOrigin<<T as Config>::AccountId>;
711
712 #[pallet::call]
713 impl<T: Config> Pallet<T> {
714 #[pallet::weight(1000)]
716 pub fn f00(_origin: OriginFor<T>) -> DispatchResult {
717 unimplemented!();
718 }
719
720 #[pallet::weight((1000, DispatchClass::Mandatory))]
721 pub fn f01(_origin: OriginFor<T>) -> DispatchResult {
722 unimplemented!();
723 }
724
725 #[pallet::weight((1000, Pays::No))]
726 pub fn f02(_origin: OriginFor<T>) -> DispatchResult {
727 unimplemented!();
728 }
729
730 #[pallet::weight((1000, DispatchClass::Operational, Pays::No))]
731 pub fn f03(_origin: OriginFor<T>) -> DispatchResult {
732 unimplemented!();
733 }
734
735 #[pallet::weight(((_a * 10 + _eb * 1) as u64, DispatchClass::Normal, Pays::Yes))]
737 pub fn f11(_origin: OriginFor<T>, _a: u32, _eb: u32) -> DispatchResult {
738 unimplemented!();
739 }
740
741 #[pallet::weight((0, DispatchClass::Operational, Pays::Yes))]
742 pub fn f12(_origin: OriginFor<T>, _a: u32, _eb: u32) -> DispatchResult {
743 unimplemented!();
744 }
745
746 #[pallet::weight(T::DbWeight::get().reads(3) + T::DbWeight::get().writes(2) + Weight::from_all(10_000))]
747 pub fn f20(_origin: OriginFor<T>) -> DispatchResult {
748 unimplemented!();
749 }
750
751 #[pallet::weight(T::DbWeight::get().reads_writes(6, 5) + Weight::from_all(40_000))]
752 pub fn f21(_origin: OriginFor<T>) -> DispatchResult {
753 unimplemented!();
754 }
755 }
756
757 pub mod pallet_prelude {
758 pub type OriginFor<T> = <T as super::Config>::RuntimeOrigin;
759
760 pub type HeaderFor<T> =
761 <<T as super::Config>::Block as sp_runtime::traits::HeaderProvider>::HeaderT;
762
763 pub type BlockNumberFor<T> = <HeaderFor<T> as sp_runtime::traits::Header>::Number;
764 }
765 }
766
767 type BlockNumber = u32;
768 type AccountId = u32;
769 type Balance = u32;
770 type Header = generic::Header<BlockNumber, BlakeTwo256>;
771 type UncheckedExtrinsic = generic::UncheckedExtrinsic<u32, RuntimeCall, (), ()>;
772 type Block = generic::Block<Header, UncheckedExtrinsic>;
773
774 crate::construct_runtime!(
775 pub enum Runtime
776 {
777 System: self::frame_system,
778 }
779 );
780
781 parameter_types! {
782 pub const DbWeight: RuntimeDbWeight = RuntimeDbWeight {
783 read: 100,
784 write: 1000,
785 };
786 }
787
788 impl Config for Runtime {
789 type Block = Block;
790 type AccountId = AccountId;
791 type Balance = Balance;
792 type BaseCallFilter = crate::traits::Everything;
793 type RuntimeOrigin = RuntimeOrigin;
794 type RuntimeCall = RuntimeCall;
795 type RuntimeTask = RuntimeTask;
796 type DbWeight = DbWeight;
797 type PalletInfo = PalletInfo;
798 }
799
800 #[test]
801 fn weights_are_correct() {
802 let info = Call::<Runtime>::f00 {}.get_dispatch_info();
804 assert_eq!(info.weight, Weight::from_parts(1000, 0));
805 assert_eq!(info.class, DispatchClass::Normal);
806 assert_eq!(info.pays_fee, Pays::Yes);
807
808 let info = Call::<Runtime>::f01 {}.get_dispatch_info();
810 assert_eq!(info.weight, Weight::from_parts(1000, 0));
811 assert_eq!(info.class, DispatchClass::Mandatory);
812 assert_eq!(info.pays_fee, Pays::Yes);
813
814 let info = Call::<Runtime>::f02 {}.get_dispatch_info();
816 assert_eq!(info.weight, Weight::from_parts(1000, 0));
817 assert_eq!(info.class, DispatchClass::Normal);
818 assert_eq!(info.pays_fee, Pays::No);
819
820 let info = Call::<Runtime>::f03 {}.get_dispatch_info();
822 assert_eq!(info.weight, Weight::from_parts(1000, 0));
823 assert_eq!(info.class, DispatchClass::Operational);
824 assert_eq!(info.pays_fee, Pays::No);
825
826 let info = Call::<Runtime>::f11 { a: 13, eb: 20 }.get_dispatch_info();
828 assert_eq!(info.weight, Weight::from_parts(150, 0)); assert_eq!(info.class, DispatchClass::Normal);
830 assert_eq!(info.pays_fee, Pays::Yes);
831
832 let info = Call::<Runtime>::f12 { a: 10, eb: 20 }.get_dispatch_info();
834 assert_eq!(info.weight, Weight::zero());
835 assert_eq!(info.class, DispatchClass::Operational);
836 assert_eq!(info.pays_fee, Pays::Yes);
837
838 let info = Call::<Runtime>::f20 {}.get_dispatch_info();
841 assert_eq!(info.weight, Weight::from_parts(12300, 10000)); assert_eq!(info.class, DispatchClass::Normal);
843 assert_eq!(info.pays_fee, Pays::Yes);
844
845 let info = Call::<Runtime>::f21 {}.get_dispatch_info();
847 assert_eq!(info.weight, Weight::from_parts(45600, 40000)); assert_eq!(info.class, DispatchClass::Normal);
849 assert_eq!(info.pays_fee, Pays::Yes);
850 }
851
852 #[test]
853 fn extract_actual_weight_works() {
854 let pre = DispatchInfo { weight: Weight::from_parts(1000, 0), ..Default::default() };
855 assert_eq!(
856 extract_actual_weight(&Ok(from_actual_ref_time(Some(7))), &pre),
857 Weight::from_parts(7, 0)
858 );
859 assert_eq!(
860 extract_actual_weight(&Ok(from_actual_ref_time(Some(1000))), &pre),
861 Weight::from_parts(1000, 0)
862 );
863 assert_eq!(
864 extract_actual_weight(
865 &Err(DispatchError::BadOrigin.with_weight(Weight::from_parts(9, 0))),
866 &pre
867 ),
868 Weight::from_parts(9, 0)
869 );
870 }
871
872 #[test]
873 fn extract_actual_weight_caps_at_pre_weight() {
874 let pre = DispatchInfo { weight: Weight::from_parts(1000, 0), ..Default::default() };
875 assert_eq!(
876 extract_actual_weight(&Ok(from_actual_ref_time(Some(1250))), &pre),
877 Weight::from_parts(1000, 0)
878 );
879 assert_eq!(
880 extract_actual_weight(
881 &Err(DispatchError::BadOrigin.with_weight(Weight::from_parts(1300, 0))),
882 &pre
883 ),
884 Weight::from_parts(1000, 0),
885 );
886 }
887
888 #[test]
889 fn extract_actual_pays_fee_works() {
890 let pre = DispatchInfo { weight: Weight::from_parts(1000, 0), ..Default::default() };
891 assert_eq!(extract_actual_pays_fee(&Ok(from_actual_ref_time(Some(7))), &pre), Pays::Yes);
892 assert_eq!(
893 extract_actual_pays_fee(&Ok(from_actual_ref_time(Some(1000)).into()), &pre),
894 Pays::Yes
895 );
896 assert_eq!(
897 extract_actual_pays_fee(&Ok(from_post_weight_info(Some(1000), Pays::Yes)), &pre),
898 Pays::Yes
899 );
900 assert_eq!(
901 extract_actual_pays_fee(&Ok(from_post_weight_info(Some(1000), Pays::No)), &pre),
902 Pays::No
903 );
904 assert_eq!(
905 extract_actual_pays_fee(
906 &Err(DispatchError::BadOrigin.with_weight(Weight::from_parts(9, 0))),
907 &pre
908 ),
909 Pays::Yes
910 );
911 assert_eq!(
912 extract_actual_pays_fee(
913 &Err(DispatchErrorWithPostInfo {
914 post_info: PostDispatchInfo { actual_weight: None, pays_fee: Pays::No },
915 error: DispatchError::BadOrigin,
916 }),
917 &pre
918 ),
919 Pays::No
920 );
921
922 let pre = DispatchInfo {
923 weight: Weight::from_parts(1000, 0),
924 pays_fee: Pays::No,
925 ..Default::default()
926 };
927 assert_eq!(extract_actual_pays_fee(&Ok(from_actual_ref_time(Some(7))), &pre), Pays::No);
928 assert_eq!(extract_actual_pays_fee(&Ok(from_actual_ref_time(Some(1000))), &pre), Pays::No);
929 assert_eq!(
930 extract_actual_pays_fee(&Ok(from_post_weight_info(Some(1000), Pays::Yes)), &pre),
931 Pays::No
932 );
933 }
934}
935
936#[cfg(test)]
937mod per_dispatch_class_tests {
938 use super::*;
939 use sp_runtime::traits::Zero;
940 use DispatchClass::*;
941
942 #[test]
943 fn add_works() {
944 let a = PerDispatchClass {
945 normal: (5, 10).into(),
946 operational: (20, 30).into(),
947 mandatory: Weight::MAX,
948 };
949 assert_eq!(
950 a.clone()
951 .add((20, 5).into(), Normal)
952 .add((10, 10).into(), Operational)
953 .add((u64::MAX, 3).into(), Mandatory),
954 PerDispatchClass {
955 normal: (25, 15).into(),
956 operational: (30, 40).into(),
957 mandatory: Weight::MAX
958 }
959 );
960 let b = a
961 .add(Weight::MAX, Normal)
962 .add(Weight::MAX, Operational)
963 .add(Weight::MAX, Mandatory);
964 assert_eq!(
965 b,
966 PerDispatchClass {
967 normal: Weight::MAX,
968 operational: Weight::MAX,
969 mandatory: Weight::MAX
970 }
971 );
972 assert_eq!(b.total(), Weight::MAX);
973 }
974
975 #[test]
976 fn accrue_works() {
977 let mut a = PerDispatchClass::default();
978
979 a.accrue((10, 15).into(), Normal);
980 assert_eq!(a.normal, (10, 15).into());
981 assert_eq!(a.total(), (10, 15).into());
982
983 a.accrue((20, 25).into(), Operational);
984 assert_eq!(a.operational, (20, 25).into());
985 assert_eq!(a.total(), (30, 40).into());
986
987 a.accrue((30, 35).into(), Mandatory);
988 assert_eq!(a.mandatory, (30, 35).into());
989 assert_eq!(a.total(), (60, 75).into());
990
991 a.accrue((u64::MAX, 10).into(), Operational);
992 assert_eq!(a.operational, (u64::MAX, 35).into());
993 assert_eq!(a.total(), (u64::MAX, 85).into());
994
995 a.accrue((10, u64::MAX).into(), Normal);
996 assert_eq!(a.normal, (20, u64::MAX).into());
997 assert_eq!(a.total(), Weight::MAX);
998 }
999
1000 #[test]
1001 fn reduce_works() {
1002 let mut a = PerDispatchClass {
1003 normal: (10, u64::MAX).into(),
1004 mandatory: (u64::MAX, 10).into(),
1005 operational: (20, 20).into(),
1006 };
1007
1008 a.reduce((5, 100).into(), Normal);
1009 assert_eq!(a.normal, (5, u64::MAX - 100).into());
1010 assert_eq!(a.total(), (u64::MAX, u64::MAX - 70).into());
1011
1012 a.reduce((15, 5).into(), Operational);
1013 assert_eq!(a.operational, (5, 15).into());
1014 assert_eq!(a.total(), (u64::MAX, u64::MAX - 75).into());
1015
1016 a.reduce((50, 0).into(), Mandatory);
1017 assert_eq!(a.mandatory, (u64::MAX - 50, 10).into());
1018 assert_eq!(a.total(), (u64::MAX - 40, u64::MAX - 75).into());
1019
1020 a.reduce((u64::MAX, 100).into(), Operational);
1021 assert!(a.operational.is_zero());
1022 assert_eq!(a.total(), (u64::MAX - 45, u64::MAX - 90).into());
1023
1024 a.reduce((5, u64::MAX).into(), Normal);
1025 assert!(a.normal.is_zero());
1026 assert_eq!(a.total(), (u64::MAX - 50, 10).into());
1027 }
1028
1029 #[test]
1030 fn checked_accrue_works() {
1031 let mut a = PerDispatchClass::default();
1032
1033 a.checked_accrue((1, 2).into(), Normal).unwrap();
1034 a.checked_accrue((3, 4).into(), Operational).unwrap();
1035 a.checked_accrue((5, 6).into(), Mandatory).unwrap();
1036 a.checked_accrue((7, 8).into(), Operational).unwrap();
1037 a.checked_accrue((9, 0).into(), Normal).unwrap();
1038
1039 assert_eq!(
1040 a,
1041 PerDispatchClass {
1042 normal: (10, 2).into(),
1043 operational: (10, 12).into(),
1044 mandatory: (5, 6).into(),
1045 }
1046 );
1047
1048 a.checked_accrue((u64::MAX - 10, u64::MAX - 2).into(), Normal).unwrap();
1049 a.checked_accrue((0, 0).into(), Normal).unwrap();
1050 a.checked_accrue((1, 0).into(), Normal).unwrap_err();
1051 a.checked_accrue((0, 1).into(), Normal).unwrap_err();
1052
1053 assert_eq!(
1054 a,
1055 PerDispatchClass {
1056 normal: Weight::MAX,
1057 operational: (10, 12).into(),
1058 mandatory: (5, 6).into(),
1059 }
1060 );
1061 }
1062
1063 #[test]
1064 fn checked_accrue_does_not_modify_on_error() {
1065 let mut a = PerDispatchClass {
1066 normal: 0.into(),
1067 operational: Weight::MAX / 2 + 2.into(),
1068 mandatory: 10.into(),
1069 };
1070
1071 a.checked_accrue(Weight::MAX / 2, Operational).unwrap_err();
1072 a.checked_accrue(Weight::MAX - 9.into(), Mandatory).unwrap_err();
1073 a.checked_accrue(Weight::MAX, Normal).unwrap(); assert_eq!(
1076 a,
1077 PerDispatchClass {
1078 normal: Weight::MAX,
1079 operational: Weight::MAX / 2 + 2.into(),
1080 mandatory: 10.into(),
1081 }
1082 );
1083 }
1084
1085 #[test]
1086 fn total_works() {
1087 assert!(PerDispatchClass::default().total().is_zero());
1088
1089 assert_eq!(
1090 PerDispatchClass {
1091 normal: 0.into(),
1092 operational: (10, 20).into(),
1093 mandatory: (20, u64::MAX).into(),
1094 }
1095 .total(),
1096 (30, u64::MAX).into()
1097 );
1098
1099 assert_eq!(
1100 PerDispatchClass {
1101 normal: (u64::MAX - 10, 10).into(),
1102 operational: (3, u64::MAX).into(),
1103 mandatory: (4, u64::MAX).into(),
1104 }
1105 .total(),
1106 (u64::MAX - 3, u64::MAX).into()
1107 );
1108 }
1109}