1use crate::{limits::BlockWeights, Config, Pallet, LOG_TARGET};
19use codec::{Decode, Encode};
20use frame_support::{
21 dispatch::{DispatchInfo, PostDispatchInfo},
22 traits::Get,
23};
24use scale_info::TypeInfo;
25use sp_runtime::{
26 traits::{DispatchInfoOf, Dispatchable, PostDispatchInfoOf, SignedExtension},
27 transaction_validity::{InvalidTransaction, TransactionValidity, TransactionValidityError},
28 DispatchResult,
29};
30use sp_weights::Weight;
31
32#[derive(Encode, Decode, Clone, Eq, PartialEq, Default, TypeInfo)]
39#[scale_info(skip_type_params(T))]
40pub struct CheckWeight<T: Config + Send + Sync>(core::marker::PhantomData<T>);
41
42impl<T: Config + Send + Sync> CheckWeight<T>
43where
44 T::RuntimeCall: Dispatchable<Info = DispatchInfo, PostInfo = PostDispatchInfo>,
45{
46 fn check_extrinsic_weight(
49 info: &DispatchInfoOf<T::RuntimeCall>,
50 ) -> Result<(), TransactionValidityError> {
51 let max = T::BlockWeights::get().get(info.class).max_extrinsic;
52 match max {
53 Some(max) if info.weight.any_gt(max) => {
54 log::debug!(
55 target: LOG_TARGET,
56 "Extrinsic {} is greater than the max extrinsic {}",
57 info.weight,
58 max,
59 );
60
61 Err(InvalidTransaction::ExhaustsResources.into())
62 },
63 _ => Ok(()),
64 }
65 }
66
67 fn check_block_length(
71 info: &DispatchInfoOf<T::RuntimeCall>,
72 len: usize,
73 ) -> Result<u32, TransactionValidityError> {
74 let length_limit = T::BlockLength::get();
75 let current_len = Pallet::<T>::all_extrinsics_len();
76 let added_len = len as u32;
77 let next_len = current_len.saturating_add(added_len);
78 if next_len > *length_limit.max.get(info.class) {
79 log::debug!(
80 target: LOG_TARGET,
81 "Exceeded block length limit: {} > {}",
82 next_len,
83 length_limit.max.get(info.class),
84 );
85
86 Err(InvalidTransaction::ExhaustsResources.into())
87 } else {
88 Ok(next_len)
89 }
90 }
91
92 pub fn new() -> Self {
94 Self(Default::default())
95 }
96
97 pub fn do_pre_dispatch(
101 info: &DispatchInfoOf<T::RuntimeCall>,
102 len: usize,
103 ) -> Result<(), TransactionValidityError> {
104 let next_len = Self::check_block_length(info, len)?;
105
106 let all_weight = Pallet::<T>::block_weight();
107 let maximum_weight = T::BlockWeights::get();
108 let next_weight =
109 calculate_consumed_weight::<T::RuntimeCall>(&maximum_weight, all_weight, info, len)?;
110 Self::check_extrinsic_weight(info)?;
111
112 crate::AllExtrinsicsLen::<T>::put(next_len);
113 crate::BlockWeight::<T>::put(next_weight);
114 Ok(())
115 }
116
117 pub fn do_validate(info: &DispatchInfoOf<T::RuntimeCall>, len: usize) -> TransactionValidity {
121 let _ = Self::check_block_length(info, len)?;
123 Self::check_extrinsic_weight(info)?;
127
128 Ok(Default::default())
129 }
130}
131
132pub fn calculate_consumed_weight<Call>(
136 maximum_weight: &BlockWeights,
137 mut all_weight: crate::ConsumedWeight,
138 info: &DispatchInfoOf<Call>,
139 len: usize,
140) -> Result<crate::ConsumedWeight, TransactionValidityError>
141where
142 Call: Dispatchable<Info = DispatchInfo, PostInfo = PostDispatchInfo>,
143{
144 let extrinsic_weight = info
146 .weight
147 .saturating_add(maximum_weight.get(info.class).base_extrinsic)
148 .saturating_add(Weight::from_parts(0, len as u64));
149 let limit_per_class = maximum_weight.get(info.class);
150
151 if limit_per_class.max_total.is_none() && limit_per_class.reserved.is_none() {
153 all_weight.accrue(extrinsic_weight, info.class)
154 } else {
155 all_weight.checked_accrue(extrinsic_weight, info.class).map_err(|_| {
156 log::debug!(
157 target: LOG_TARGET,
158 "All weight checked add overflow.",
159 );
160
161 InvalidTransaction::ExhaustsResources
162 })?;
163 }
164
165 let per_class = *all_weight.get(info.class);
166
167 match limit_per_class.max_total {
169 Some(max) if per_class.any_gt(max) => {
170 log::debug!(
171 target: LOG_TARGET,
172 "Exceeded the per-class allowance.",
173 );
174
175 return Err(InvalidTransaction::ExhaustsResources.into());
176 },
177 _ => {},
180 }
181
182 if all_weight.total().any_gt(maximum_weight.max_block) {
185 match limit_per_class.reserved {
186 Some(reserved) if per_class.any_gt(reserved) => {
188 log::debug!(
189 target: LOG_TARGET,
190 "Total block weight is exceeded.",
191 );
192
193 return Err(InvalidTransaction::ExhaustsResources.into());
194 },
195 _ => {},
198 }
199 }
200
201 Ok(all_weight)
202}
203
204impl<T: Config + Send + Sync> SignedExtension for CheckWeight<T>
205where
206 T::RuntimeCall: Dispatchable<Info = DispatchInfo, PostInfo = PostDispatchInfo>,
207{
208 type AccountId = T::AccountId;
209 type Call = T::RuntimeCall;
210 type AdditionalSigned = ();
211 type Pre = ();
212 const IDENTIFIER: &'static str = "CheckWeight";
213
214 fn additional_signed(&self) -> core::result::Result<(), TransactionValidityError> {
215 Ok(())
216 }
217
218 fn pre_dispatch(
219 self,
220 _who: &Self::AccountId,
221 _call: &Self::Call,
222 info: &DispatchInfoOf<Self::Call>,
223 len: usize,
224 ) -> Result<(), TransactionValidityError> {
225 Self::do_pre_dispatch(info, len)
226 }
227
228 fn validate(
229 &self,
230 _who: &Self::AccountId,
231 _call: &Self::Call,
232 info: &DispatchInfoOf<Self::Call>,
233 len: usize,
234 ) -> TransactionValidity {
235 Self::do_validate(info, len)
236 }
237
238 fn pre_dispatch_unsigned(
239 _call: &Self::Call,
240 info: &DispatchInfoOf<Self::Call>,
241 len: usize,
242 ) -> Result<(), TransactionValidityError> {
243 Self::do_pre_dispatch(info, len)
244 }
245
246 fn validate_unsigned(
247 _call: &Self::Call,
248 info: &DispatchInfoOf<Self::Call>,
249 len: usize,
250 ) -> TransactionValidity {
251 Self::do_validate(info, len)
252 }
253
254 fn post_dispatch(
255 _pre: Option<Self::Pre>,
256 info: &DispatchInfoOf<Self::Call>,
257 post_info: &PostDispatchInfoOf<Self::Call>,
258 _len: usize,
259 _result: &DispatchResult,
260 ) -> Result<(), TransactionValidityError> {
261 let unspent = post_info.calc_unspent(info);
262 if unspent.any_gt(Weight::zero()) {
263 crate::BlockWeight::<T>::mutate(|current_weight| {
264 current_weight.reduce(unspent, info.class);
265 })
266 }
267
268 log::trace!(
269 target: LOG_TARGET,
270 "Used block weight: {:?}",
271 crate::BlockWeight::<T>::get(),
272 );
273
274 log::trace!(
275 target: LOG_TARGET,
276 "Used block length: {:?}",
277 Pallet::<T>::all_extrinsics_len(),
278 );
279
280 Ok(())
281 }
282}
283
284impl<T: Config + Send + Sync> core::fmt::Debug for CheckWeight<T> {
285 #[cfg(feature = "std")]
286 fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
287 write!(f, "CheckWeight")
288 }
289
290 #[cfg(not(feature = "std"))]
291 fn fmt(&self, _: &mut core::fmt::Formatter) -> core::fmt::Result {
292 Ok(())
293 }
294}
295
296#[cfg(test)]
297mod tests {
298 use super::*;
299 use crate::{
300 mock::{new_test_ext, System, Test, CALL},
301 AllExtrinsicsLen, BlockWeight, DispatchClass,
302 };
303 use core::marker::PhantomData;
304 use frame_support::{assert_err, assert_ok, dispatch::Pays, weights::Weight};
305
306 fn block_weights() -> crate::limits::BlockWeights {
307 <Test as crate::Config>::BlockWeights::get()
308 }
309
310 fn normal_weight_limit() -> Weight {
311 block_weights()
312 .get(DispatchClass::Normal)
313 .max_total
314 .unwrap_or_else(|| block_weights().max_block)
315 }
316
317 fn block_weight_limit() -> Weight {
318 block_weights().max_block
319 }
320
321 fn normal_length_limit() -> u32 {
322 *<Test as Config>::BlockLength::get().max.get(DispatchClass::Normal)
323 }
324
325 #[test]
326 fn mandatory_extrinsic_doesnt_care_about_limits() {
327 fn check(call: impl FnOnce(&DispatchInfo, usize)) {
328 new_test_ext().execute_with(|| {
329 let max = DispatchInfo {
330 weight: Weight::MAX,
331 class: DispatchClass::Mandatory,
332 ..Default::default()
333 };
334 let len = 0_usize;
335
336 call(&max, len);
337 });
338 }
339
340 check(|max, len| {
341 assert_ok!(CheckWeight::<Test>::do_pre_dispatch(max, len));
342 assert_eq!(System::block_weight().total(), Weight::MAX);
343 assert!(System::block_weight().total().ref_time() > block_weight_limit().ref_time());
344 });
345 check(|max, len| {
346 assert_ok!(CheckWeight::<Test>::do_validate(max, len));
347 });
348 }
349
350 #[test]
351 fn normal_extrinsic_limited_by_maximum_extrinsic_weight() {
352 new_test_ext().execute_with(|| {
353 let max = DispatchInfo {
354 weight: block_weights().get(DispatchClass::Normal).max_extrinsic.unwrap() +
355 Weight::from_parts(1, 0),
356 class: DispatchClass::Normal,
357 ..Default::default()
358 };
359 let len = 0_usize;
360 assert_err!(
361 CheckWeight::<Test>::do_validate(&max, len),
362 InvalidTransaction::ExhaustsResources
363 );
364 });
365 }
366
367 #[test]
368 fn operational_extrinsic_limited_by_operational_space_limit() {
369 new_test_ext().execute_with(|| {
370 let weights = block_weights();
371 let operational_limit = weights
372 .get(DispatchClass::Operational)
373 .max_total
374 .unwrap_or_else(|| weights.max_block);
375 let base_weight = weights.get(DispatchClass::Operational).base_extrinsic;
376
377 let weight = operational_limit - base_weight;
378 let okay =
379 DispatchInfo { weight, class: DispatchClass::Operational, ..Default::default() };
380 let max = DispatchInfo {
381 weight: weight + Weight::from_parts(1, 0),
382 class: DispatchClass::Operational,
383 ..Default::default()
384 };
385 let len = 0_usize;
386
387 assert_eq!(CheckWeight::<Test>::do_validate(&okay, len), Ok(Default::default()));
388 assert_err!(
389 CheckWeight::<Test>::do_validate(&max, len),
390 InvalidTransaction::ExhaustsResources
391 );
392 });
393 }
394
395 #[test]
396 fn register_extra_weight_unchecked_doesnt_care_about_limits() {
397 new_test_ext().execute_with(|| {
398 System::register_extra_weight_unchecked(Weight::MAX, DispatchClass::Normal);
399 assert_eq!(System::block_weight().total(), Weight::MAX);
400 assert!(System::block_weight().total().ref_time() > block_weight_limit().ref_time());
401 });
402 }
403
404 #[test]
405 fn full_block_with_normal_and_operational() {
406 new_test_ext().execute_with(|| {
407 let max_normal =
413 DispatchInfo { weight: Weight::from_parts(753, 0), ..Default::default() };
414 let rest_operational = DispatchInfo {
415 weight: Weight::from_parts(246, 0),
416 class: DispatchClass::Operational,
417 ..Default::default()
418 };
419
420 let len = 0_usize;
421
422 assert_ok!(CheckWeight::<Test>::do_pre_dispatch(&max_normal, len));
423 assert_eq!(System::block_weight().total(), Weight::from_parts(768, 0));
424 assert_ok!(CheckWeight::<Test>::do_pre_dispatch(&rest_operational, len));
425 assert_eq!(block_weight_limit(), Weight::from_parts(1024, u64::MAX));
426 assert_eq!(System::block_weight().total(), block_weight_limit().set_proof_size(0));
427 assert_eq!(CheckWeight::<Test>::check_extrinsic_weight(&rest_operational), Ok(()));
429 });
430 }
431
432 #[test]
433 fn dispatch_order_does_not_effect_weight_logic() {
434 new_test_ext().execute_with(|| {
435 let max_normal =
437 DispatchInfo { weight: Weight::from_parts(753, 0), ..Default::default() };
438 let rest_operational = DispatchInfo {
439 weight: Weight::from_parts(246, 0),
440 class: DispatchClass::Operational,
441 ..Default::default()
442 };
443
444 let len = 0_usize;
445
446 assert_ok!(CheckWeight::<Test>::do_pre_dispatch(&rest_operational, len));
447 assert_eq!(System::block_weight().total(), Weight::from_parts(266, 0));
449 assert_ok!(CheckWeight::<Test>::do_pre_dispatch(&max_normal, len));
450 assert_eq!(block_weight_limit(), Weight::from_parts(1024, u64::MAX));
451 assert_eq!(System::block_weight().total(), block_weight_limit().set_proof_size(0));
452 });
453 }
454
455 #[test]
456 fn operational_works_on_full_block() {
457 new_test_ext().execute_with(|| {
458 System::register_extra_weight_unchecked(Weight::MAX, DispatchClass::Mandatory);
460 let dispatch_normal = DispatchInfo {
461 weight: Weight::from_parts(251, 0),
462 class: DispatchClass::Normal,
463 ..Default::default()
464 };
465 let dispatch_operational = DispatchInfo {
466 weight: Weight::from_parts(246, 0),
467 class: DispatchClass::Operational,
468 ..Default::default()
469 };
470 let len = 0_usize;
471
472 assert_err!(
473 CheckWeight::<Test>::do_pre_dispatch(&dispatch_normal, len),
474 InvalidTransaction::ExhaustsResources
475 );
476 assert_ok!(CheckWeight::<Test>::do_pre_dispatch(&dispatch_operational, len));
479 assert_err!(
481 CheckWeight::<Test>::do_pre_dispatch(&dispatch_operational, len),
482 InvalidTransaction::ExhaustsResources
483 );
484 assert_eq!(CheckWeight::<Test>::check_extrinsic_weight(&dispatch_operational), Ok(()));
486 });
487 }
488
489 #[test]
490 fn signed_ext_check_weight_works_operational_tx() {
491 new_test_ext().execute_with(|| {
492 let normal = DispatchInfo { weight: Weight::from_parts(100, 0), ..Default::default() };
493 let op = DispatchInfo {
494 weight: Weight::from_parts(100, 0),
495 class: DispatchClass::Operational,
496 pays_fee: Pays::Yes,
497 };
498 let len = 0_usize;
499 let normal_limit = normal_weight_limit();
500
501 BlockWeight::<Test>::mutate(|current_weight| {
503 current_weight.set(normal_limit, DispatchClass::Normal)
504 });
505 assert_err!(
507 CheckWeight::<Test>(PhantomData).pre_dispatch(&1, CALL, &normal, len),
508 InvalidTransaction::ExhaustsResources
509 );
510 assert_ok!(CheckWeight::<Test>(PhantomData).pre_dispatch(&1, CALL, &op, len));
512
513 let len = 100_usize;
515 AllExtrinsicsLen::<Test>::put(normal_length_limit());
516 assert_err!(
517 CheckWeight::<Test>(PhantomData).pre_dispatch(&1, CALL, &normal, len),
518 InvalidTransaction::ExhaustsResources
519 );
520 assert_ok!(CheckWeight::<Test>(PhantomData).pre_dispatch(&1, CALL, &op, len));
521 })
522 }
523
524 #[test]
525 fn signed_ext_check_weight_block_size_works() {
526 new_test_ext().execute_with(|| {
527 let normal = DispatchInfo::default();
528 let normal_limit = normal_weight_limit().ref_time() as usize;
529 let reset_check_weight = |tx, s, f| {
530 AllExtrinsicsLen::<Test>::put(0);
531 let r = CheckWeight::<Test>(PhantomData).pre_dispatch(&1, CALL, tx, s);
532 if f {
533 assert!(r.is_err())
534 } else {
535 assert!(r.is_ok())
536 }
537 };
538
539 reset_check_weight(&normal, normal_limit - 1, false);
540 reset_check_weight(&normal, normal_limit, false);
541 reset_check_weight(&normal, normal_limit + 1, true);
542
543 let op = DispatchInfo {
545 weight: Weight::zero(),
546 class: DispatchClass::Operational,
547 pays_fee: Pays::Yes,
548 };
549 reset_check_weight(&op, normal_limit, false);
550 reset_check_weight(&op, normal_limit + 100, false);
551 reset_check_weight(&op, 1024, false);
552 reset_check_weight(&op, 1025, true);
553 })
554 }
555
556 #[test]
557 fn signed_ext_check_weight_works_normal_tx() {
558 new_test_ext().execute_with(|| {
559 let normal_limit = normal_weight_limit();
560 let small = DispatchInfo { weight: Weight::from_parts(100, 0), ..Default::default() };
561 let base_extrinsic = block_weights().get(DispatchClass::Normal).base_extrinsic;
562 let medium =
563 DispatchInfo { weight: normal_limit - base_extrinsic, ..Default::default() };
564 let big = DispatchInfo {
565 weight: normal_limit - base_extrinsic + Weight::from_parts(1, 0),
566 ..Default::default()
567 };
568 let len = 0_usize;
569
570 let reset_check_weight = |i, f, s| {
571 BlockWeight::<Test>::mutate(|current_weight| {
572 current_weight.set(s, DispatchClass::Normal)
573 });
574 let r = CheckWeight::<Test>(PhantomData).pre_dispatch(&1, CALL, i, len);
575 if f {
576 assert!(r.is_err())
577 } else {
578 assert!(r.is_ok())
579 }
580 };
581
582 reset_check_weight(&small, false, Weight::zero());
583 reset_check_weight(&medium, false, Weight::zero());
584 reset_check_weight(&big, true, Weight::from_parts(1, 0));
585 })
586 }
587
588 #[test]
589 fn signed_ext_check_weight_refund_works() {
590 new_test_ext().execute_with(|| {
591 let info = DispatchInfo { weight: Weight::from_parts(512, 0), ..Default::default() };
593 let post_info = PostDispatchInfo {
594 actual_weight: Some(Weight::from_parts(128, 0)),
595 pays_fee: Default::default(),
596 };
597 let len = 0_usize;
598 let base_extrinsic = block_weights().get(DispatchClass::Normal).base_extrinsic;
599
600 BlockWeight::<Test>::mutate(|current_weight| {
602 current_weight.set(Weight::zero(), DispatchClass::Mandatory);
603 current_weight
604 .set(Weight::from_parts(256, 0) - base_extrinsic, DispatchClass::Normal);
605 });
606
607 let pre = CheckWeight::<Test>(PhantomData).pre_dispatch(&1, CALL, &info, len).unwrap();
608 assert_eq!(
609 BlockWeight::<Test>::get().total(),
610 info.weight + Weight::from_parts(256, 0)
611 );
612
613 assert_ok!(CheckWeight::<Test>::post_dispatch(
614 Some(pre),
615 &info,
616 &post_info,
617 len,
618 &Ok(())
619 ));
620 assert_eq!(
621 BlockWeight::<Test>::get().total(),
622 post_info.actual_weight.unwrap() + Weight::from_parts(256, 0)
623 );
624 })
625 }
626
627 #[test]
628 fn signed_ext_check_weight_actual_weight_higher_than_max_is_capped() {
629 new_test_ext().execute_with(|| {
630 let info = DispatchInfo { weight: Weight::from_parts(512, 0), ..Default::default() };
631 let post_info = PostDispatchInfo {
632 actual_weight: Some(Weight::from_parts(700, 0)),
633 pays_fee: Default::default(),
634 };
635 let len = 0_usize;
636
637 BlockWeight::<Test>::mutate(|current_weight| {
638 current_weight.set(Weight::zero(), DispatchClass::Mandatory);
639 current_weight.set(Weight::from_parts(128, 0), DispatchClass::Normal);
640 });
641
642 let pre = CheckWeight::<Test>(PhantomData).pre_dispatch(&1, CALL, &info, len).unwrap();
643 assert_eq!(
644 BlockWeight::<Test>::get().total(),
645 info.weight +
646 Weight::from_parts(128, 0) +
647 block_weights().get(DispatchClass::Normal).base_extrinsic,
648 );
649
650 assert_ok!(CheckWeight::<Test>::post_dispatch(
651 Some(pre),
652 &info,
653 &post_info,
654 len,
655 &Ok(())
656 ));
657 assert_eq!(
658 BlockWeight::<Test>::get().total(),
659 info.weight +
660 Weight::from_parts(128, 0) +
661 block_weights().get(DispatchClass::Normal).base_extrinsic,
662 );
663 })
664 }
665
666 #[test]
667 fn zero_weight_extrinsic_still_has_base_weight() {
668 new_test_ext().execute_with(|| {
669 let weights = block_weights();
670 let free = DispatchInfo { weight: Weight::zero(), ..Default::default() };
671 let len = 0_usize;
672
673 assert_eq!(System::block_weight().total(), weights.base_block);
675 assert_ok!(CheckWeight::<Test>(PhantomData).pre_dispatch(&1, CALL, &free, len));
676 assert_eq!(
677 System::block_weight().total(),
678 weights.get(DispatchClass::Normal).base_extrinsic + weights.base_block
679 );
680 })
681 }
682
683 #[test]
684 fn normal_and_mandatory_tracked_separately() {
685 new_test_ext().execute_with(|| {
686 let max_normal =
690 DispatchInfo { weight: Weight::from_parts(753, 0), ..Default::default() };
691 let mandatory = DispatchInfo {
692 weight: Weight::from_parts(1019, 0),
693 class: DispatchClass::Mandatory,
694 ..Default::default()
695 };
696
697 let len = 0_usize;
698
699 assert_ok!(CheckWeight::<Test>::do_pre_dispatch(&max_normal, len));
700 assert_eq!(System::block_weight().total(), Weight::from_parts(768, 0));
701 assert_ok!(CheckWeight::<Test>::do_pre_dispatch(&mandatory, len));
702 assert_eq!(block_weight_limit(), Weight::from_parts(1024, u64::MAX));
703 assert_eq!(System::block_weight().total(), Weight::from_parts(1024 + 768, 0));
704 assert_eq!(CheckWeight::<Test>::check_extrinsic_weight(&mandatory), Ok(()));
705 });
706 }
707
708 #[test]
709 fn no_max_total_should_still_be_limited_by_max_block() {
710 let maximum_weight = BlockWeights::builder()
712 .base_block(Weight::zero())
713 .for_class(DispatchClass::non_mandatory(), |w| {
714 w.base_extrinsic = Weight::zero();
715 w.max_total = Some(Weight::from_parts(20, u64::MAX));
716 })
717 .for_class(DispatchClass::Mandatory, |w| {
718 w.base_extrinsic = Weight::zero();
719 w.reserved = Some(Weight::from_parts(5, u64::MAX));
720 w.max_total = None;
721 })
722 .build_or_panic();
723 let all_weight = crate::ConsumedWeight::new(|class| match class {
724 DispatchClass::Normal => Weight::from_parts(10, 0),
725 DispatchClass::Operational => Weight::from_parts(10, 0),
726 DispatchClass::Mandatory => Weight::zero(),
727 });
728 assert_eq!(maximum_weight.max_block, all_weight.total().set_proof_size(u64::MAX));
729
730 let mandatory1 = DispatchInfo {
732 weight: Weight::from_parts(5, 0),
733 class: DispatchClass::Mandatory,
734 ..Default::default()
735 };
736 let mandatory2 = DispatchInfo {
738 weight: Weight::from_parts(6, 0),
739 class: DispatchClass::Mandatory,
740 ..Default::default()
741 };
742
743 assert_ok!(calculate_consumed_weight::<<Test as Config>::RuntimeCall>(
745 &maximum_weight,
746 all_weight.clone(),
747 &mandatory1,
748 0
749 ));
750 assert_err!(
751 calculate_consumed_weight::<<Test as Config>::RuntimeCall>(
752 &maximum_weight,
753 all_weight,
754 &mandatory2,
755 0
756 ),
757 InvalidTransaction::ExhaustsResources
758 );
759 }
760
761 #[test]
762 fn proof_size_includes_length() {
763 let maximum_weight = BlockWeights::builder()
764 .base_block(Weight::zero())
765 .for_class(DispatchClass::non_mandatory(), |w| {
766 w.base_extrinsic = Weight::zero();
767 w.max_total = Some(Weight::from_parts(20, 1000));
768 })
769 .for_class(DispatchClass::Mandatory, |w| {
770 w.base_extrinsic = Weight::zero();
771 w.max_total = Some(Weight::from_parts(20, 1000));
772 })
773 .build_or_panic();
774 let all_weight = crate::ConsumedWeight::new(|class| match class {
775 DispatchClass::Normal => Weight::from_parts(5, 0),
776 DispatchClass::Operational => Weight::from_parts(5, 0),
777 DispatchClass::Mandatory => Weight::from_parts(0, 0),
778 });
779
780 let normal = DispatchInfo {
781 weight: Weight::from_parts(5, 0),
782 class: DispatchClass::Normal,
783 ..Default::default()
784 };
785
786 let mandatory = DispatchInfo {
787 weight: Weight::from_parts(5, 0),
788 class: DispatchClass::Mandatory,
789 ..Default::default()
790 };
791
792 let consumed = calculate_consumed_weight::<<Test as Config>::RuntimeCall>(
794 &maximum_weight,
795 all_weight.clone(),
796 &normal,
797 0,
798 )
799 .unwrap();
800
801 assert_eq!(consumed.total().saturating_sub(all_weight.total()), normal.weight);
802
803 let consumed = calculate_consumed_weight::<<Test as Config>::RuntimeCall>(
804 &maximum_weight,
805 all_weight.clone(),
806 &mandatory,
807 0,
808 )
809 .unwrap();
810 assert_eq!(consumed.total().saturating_sub(all_weight.total()), mandatory.weight);
811
812 let consumed = calculate_consumed_weight::<<Test as Config>::RuntimeCall>(
814 &maximum_weight,
815 all_weight.clone(),
816 &normal,
817 100,
818 )
819 .unwrap();
820 assert_eq!(
822 consumed.total().saturating_sub(all_weight.total()),
823 normal.weight.add_proof_size(100)
824 );
825
826 let consumed = calculate_consumed_weight::<<Test as Config>::RuntimeCall>(
827 &maximum_weight,
828 all_weight.clone(),
829 &mandatory,
830 100,
831 )
832 .unwrap();
833 assert_eq!(
835 consumed.total().saturating_sub(all_weight.total()),
836 mandatory.weight.add_proof_size(100)
837 );
838
839 let consumed = calculate_consumed_weight::<<Test as Config>::RuntimeCall>(
841 &maximum_weight,
842 all_weight.clone(),
843 &normal,
844 2000,
845 );
846 assert_eq!(consumed, Err(InvalidTransaction::ExhaustsResources.into()));
848
849 let consumed = calculate_consumed_weight::<<Test as Config>::RuntimeCall>(
851 &maximum_weight,
852 all_weight.clone(),
853 &mandatory,
854 2000,
855 );
856 assert_eq!(consumed, Err(InvalidTransaction::ExhaustsResources.into()));
858 }
859}