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