1use crate::{limits::BlockWeights, Config, Pallet, LOG_TARGET};
19use codec::{Decode, DecodeWithMemTracking, Encode};
20use frame_support::{
21 dispatch::{DispatchInfo, PostDispatchInfo},
22 pallet_prelude::TransactionSource,
23 traits::Get,
24};
25use scale_info::TypeInfo;
26use sp_runtime::{
27 traits::{
28 DispatchInfoOf, Dispatchable, PostDispatchInfoOf, TransactionExtension, ValidateResult,
29 },
30 transaction_validity::{InvalidTransaction, TransactionValidityError, ValidTransaction},
31 DispatchResult,
32};
33use sp_weights::Weight;
34
35#[derive(Encode, Decode, DecodeWithMemTracking, Clone, Eq, PartialEq, Default, TypeInfo)]
42#[scale_info(skip_type_params(T))]
43pub struct CheckWeight<T: Config + Send + Sync>(core::marker::PhantomData<T>);
44
45impl<T: Config + Send + Sync> CheckWeight<T>
46where
47 T::RuntimeCall: Dispatchable<Info = DispatchInfo, PostInfo = PostDispatchInfo>,
48{
49 fn check_extrinsic_weight(
52 info: &DispatchInfoOf<T::RuntimeCall>,
53 len: usize,
54 ) -> Result<(), TransactionValidityError> {
55 let max = T::BlockWeights::get().get(info.class).max_extrinsic;
56 let total_weight_including_length =
57 info.total_weight().saturating_add_proof_size(len as u64);
58 match max {
59 Some(max) if total_weight_including_length.any_gt(max) => {
60 log::debug!(
61 target: LOG_TARGET,
62 "Extrinsic with length included {} is greater than the max extrinsic {}",
63 total_weight_including_length,
64 max,
65 );
66
67 Err(InvalidTransaction::ExhaustsResources.into())
68 },
69 _ => Ok(()),
70 }
71 }
72
73 fn check_block_length(
77 info: &DispatchInfoOf<T::RuntimeCall>,
78 len: usize,
79 ) -> Result<u32, TransactionValidityError> {
80 let length_limit = T::BlockLength::get();
81 let current_len = Pallet::<T>::all_extrinsics_len();
82 let added_len = len as u32;
83 let next_len = current_len.saturating_add(added_len);
84 if next_len > *length_limit.max.get(info.class) {
85 log::debug!(
86 target: LOG_TARGET,
87 "Exceeded block length limit: {} > {}",
88 next_len,
89 length_limit.max.get(info.class),
90 );
91
92 Err(InvalidTransaction::ExhaustsResources.into())
93 } else {
94 Ok(next_len)
95 }
96 }
97
98 pub fn new() -> Self {
100 Self(Default::default())
101 }
102
103 pub fn do_validate(
109 info: &DispatchInfoOf<T::RuntimeCall>,
110 len: usize,
111 ) -> Result<(ValidTransaction, u32), TransactionValidityError> {
112 let next_len = Self::check_block_length(info, len)?;
114 Self::check_extrinsic_weight(info, len)?;
118
119 Ok((Default::default(), next_len))
120 }
121
122 pub fn do_prepare(
126 info: &DispatchInfoOf<T::RuntimeCall>,
127 len: usize,
128 next_len: u32,
129 ) -> Result<(), TransactionValidityError> {
130 let all_weight = Pallet::<T>::block_weight();
131 let maximum_weight = T::BlockWeights::get();
132 let next_weight =
133 calculate_consumed_weight::<T::RuntimeCall>(&maximum_weight, all_weight, info, len)?;
134 crate::AllExtrinsicsLen::<T>::put(next_len);
137 crate::BlockWeight::<T>::put(next_weight);
138 Ok(())
139 }
140
141 #[deprecated(note = "Use `frame_system::Pallet::reclaim_weight` instead.")]
142 pub fn do_post_dispatch(
143 info: &DispatchInfoOf<T::RuntimeCall>,
144 post_info: &PostDispatchInfoOf<T::RuntimeCall>,
145 ) -> Result<(), TransactionValidityError> {
146 crate::Pallet::<T>::reclaim_weight(info, post_info)
147 }
148}
149
150pub fn calculate_consumed_weight<Call>(
154 maximum_weight: &BlockWeights,
155 mut all_weight: crate::ConsumedWeight,
156 info: &DispatchInfoOf<Call>,
157 len: usize,
158) -> Result<crate::ConsumedWeight, TransactionValidityError>
159where
160 Call: Dispatchable<Info = DispatchInfo, PostInfo = PostDispatchInfo>,
161{
162 let extrinsic_weight = info
164 .total_weight()
165 .saturating_add(maximum_weight.get(info.class).base_extrinsic)
166 .saturating_add(Weight::from_parts(0, len as u64));
167 let limit_per_class = maximum_weight.get(info.class);
168
169 if limit_per_class.max_total.is_none() && limit_per_class.reserved.is_none() {
171 all_weight.accrue(extrinsic_weight, info.class)
172 } else {
173 all_weight.checked_accrue(extrinsic_weight, info.class).map_err(|_| {
174 log::debug!(
175 target: LOG_TARGET,
176 "All weight checked add overflow.",
177 );
178
179 InvalidTransaction::ExhaustsResources
180 })?;
181 }
182
183 let per_class = *all_weight.get(info.class);
184
185 match limit_per_class.max_total {
187 Some(max) if per_class.any_gt(max) => {
188 log::debug!(
189 target: LOG_TARGET,
190 "Exceeded the per-class allowance.",
191 );
192
193 return Err(InvalidTransaction::ExhaustsResources.into());
194 },
195 _ => {},
198 }
199
200 if all_weight.total().any_gt(maximum_weight.max_block) {
203 match limit_per_class.reserved {
204 Some(reserved) if per_class.any_gt(reserved) => {
206 log::debug!(
207 target: LOG_TARGET,
208 "Total block weight is exceeded.",
209 );
210
211 return Err(InvalidTransaction::ExhaustsResources.into());
212 },
213 _ => {},
216 }
217 }
218
219 Ok(all_weight)
220}
221
222impl<T: Config + Send + Sync> TransactionExtension<T::RuntimeCall> for CheckWeight<T>
223where
224 T::RuntimeCall: Dispatchable<Info = DispatchInfo, PostInfo = PostDispatchInfo>,
225{
226 const IDENTIFIER: &'static str = "CheckWeight";
227 type Implicit = ();
228 type Pre = ();
229 type Val = u32; fn weight(&self, _: &T::RuntimeCall) -> Weight {
232 <T::ExtensionsWeightInfo as super::WeightInfo>::check_weight()
233 }
234
235 fn validate(
236 &self,
237 origin: T::RuntimeOrigin,
238 _call: &T::RuntimeCall,
239 info: &DispatchInfoOf<T::RuntimeCall>,
240 len: usize,
241 _self_implicit: Self::Implicit,
242 _inherited_implication: &impl Encode,
243 _source: TransactionSource,
244 ) -> ValidateResult<Self::Val, T::RuntimeCall> {
245 let (validity, next_len) = Self::do_validate(info, len)?;
246 Ok((validity, next_len, origin))
247 }
248
249 fn prepare(
250 self,
251 val: Self::Val,
252 _origin: &T::RuntimeOrigin,
253 _call: &T::RuntimeCall,
254 info: &DispatchInfoOf<T::RuntimeCall>,
255 len: usize,
256 ) -> Result<Self::Pre, TransactionValidityError> {
257 Self::do_prepare(info, len, val)
258 }
259
260 fn post_dispatch_details(
261 _pre: Self::Pre,
262 info: &DispatchInfoOf<T::RuntimeCall>,
263 post_info: &PostDispatchInfoOf<T::RuntimeCall>,
264 _len: usize,
265 _result: &DispatchResult,
266 ) -> Result<Weight, TransactionValidityError> {
267 crate::Pallet::<T>::reclaim_weight(info, post_info).map(|()| Weight::zero())
268 }
269
270 fn bare_validate(
271 _call: &T::RuntimeCall,
272 info: &DispatchInfoOf<T::RuntimeCall>,
273 len: usize,
274 ) -> frame_support::pallet_prelude::TransactionValidity {
275 Ok(Self::do_validate(info, len)?.0)
276 }
277
278 fn bare_validate_and_prepare(
279 _call: &T::RuntimeCall,
280 info: &DispatchInfoOf<T::RuntimeCall>,
281 len: usize,
282 ) -> Result<(), TransactionValidityError> {
283 let (_, next_len) = Self::do_validate(info, len)?;
284 Self::do_prepare(info, len, next_len)
285 }
286
287 fn bare_post_dispatch(
288 info: &DispatchInfoOf<T::RuntimeCall>,
289 post_info: &mut PostDispatchInfoOf<T::RuntimeCall>,
290 _len: usize,
291 _result: &DispatchResult,
292 ) -> Result<(), TransactionValidityError> {
293 crate::Pallet::<T>::reclaim_weight(info, post_info)
294 }
295}
296
297impl<T: Config + Send + Sync> core::fmt::Debug for CheckWeight<T> {
298 #[cfg(feature = "std")]
299 fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
300 write!(f, "CheckWeight")
301 }
302
303 #[cfg(not(feature = "std"))]
304 fn fmt(&self, _: &mut core::fmt::Formatter) -> core::fmt::Result {
305 Ok(())
306 }
307}
308
309#[cfg(test)]
310mod tests {
311 use super::*;
312 use crate::{
313 mock::{new_test_ext, RuntimeBlockWeights, System, Test, CALL},
314 AllExtrinsicsLen, BlockWeight, DispatchClass,
315 };
316 use core::marker::PhantomData;
317 use frame_support::{assert_err, assert_ok, dispatch::Pays, weights::Weight};
318 use sp_runtime::traits::DispatchTransaction;
319
320 fn block_weights() -> crate::limits::BlockWeights {
321 <Test as crate::Config>::BlockWeights::get()
322 }
323
324 fn normal_weight_limit() -> Weight {
325 block_weights()
326 .get(DispatchClass::Normal)
327 .max_total
328 .unwrap_or_else(|| block_weights().max_block)
329 }
330
331 fn block_weight_limit() -> Weight {
332 block_weights().max_block
333 }
334
335 fn normal_length_limit() -> u32 {
336 *<Test as Config>::BlockLength::get().max.get(DispatchClass::Normal)
337 }
338
339 #[test]
340 fn mandatory_extrinsic_doesnt_care_about_limits() {
341 fn check(call: impl FnOnce(&DispatchInfo, usize)) {
342 new_test_ext().execute_with(|| {
343 let max = DispatchInfo {
344 call_weight: Weight::MAX,
345 class: DispatchClass::Mandatory,
346 ..Default::default()
347 };
348 let len = 0_usize;
349
350 call(&max, len);
351 });
352 }
353
354 check(|max, len| {
355 let next_len = CheckWeight::<Test>::check_block_length(max, len).unwrap();
356 assert_ok!(CheckWeight::<Test>::do_prepare(max, len, next_len));
357 assert_eq!(System::block_weight().total(), Weight::MAX);
358 assert!(System::block_weight().total().ref_time() > block_weight_limit().ref_time());
359 });
360 check(|max, len| {
361 assert_ok!(CheckWeight::<Test>::do_validate(max, len));
362 });
363 }
364
365 #[test]
366 fn normal_extrinsic_limited_by_maximum_extrinsic_weight() {
367 new_test_ext().execute_with(|| {
368 let max = DispatchInfo {
369 call_weight: block_weights().get(DispatchClass::Normal).max_extrinsic.unwrap() +
370 Weight::from_parts(1, 0),
371 class: DispatchClass::Normal,
372 ..Default::default()
373 };
374 let len = 0_usize;
375 assert_err!(
376 CheckWeight::<Test>::do_validate(&max, len),
377 InvalidTransaction::ExhaustsResources
378 );
379 });
380 }
381
382 #[test]
383 fn operational_extrinsic_limited_by_operational_space_limit() {
384 new_test_ext().execute_with(|| {
385 let weights = block_weights();
386 let operational_limit = weights
387 .get(DispatchClass::Operational)
388 .max_total
389 .unwrap_or_else(|| weights.max_block);
390 let base_weight = weights.get(DispatchClass::Operational).base_extrinsic;
391
392 let call_weight = operational_limit - base_weight;
393 let okay = DispatchInfo {
394 call_weight,
395 class: DispatchClass::Operational,
396 ..Default::default()
397 };
398 let max = DispatchInfo {
399 call_weight: call_weight + Weight::from_parts(1, 0),
400 class: DispatchClass::Operational,
401 ..Default::default()
402 };
403 let len = 0_usize;
404
405 assert_eq!(CheckWeight::<Test>::do_validate(&okay, len), Ok(Default::default()));
406 assert_err!(
407 CheckWeight::<Test>::do_validate(&max, len),
408 InvalidTransaction::ExhaustsResources
409 );
410 });
411 }
412
413 #[test]
414 fn register_extra_weight_unchecked_doesnt_care_about_limits() {
415 new_test_ext().execute_with(|| {
416 System::register_extra_weight_unchecked(Weight::MAX, DispatchClass::Normal);
417 assert_eq!(System::block_weight().total(), Weight::MAX);
418 assert!(System::block_weight().total().ref_time() > block_weight_limit().ref_time());
419 });
420 }
421
422 #[test]
423 fn full_block_with_normal_and_operational() {
424 new_test_ext().execute_with(|| {
425 let max_normal =
431 DispatchInfo { call_weight: Weight::from_parts(753, 0), ..Default::default() };
432 let rest_operational = DispatchInfo {
433 call_weight: Weight::from_parts(246, 0),
434 class: DispatchClass::Operational,
435 ..Default::default()
436 };
437
438 let len = 0_usize;
439
440 let next_len = CheckWeight::<Test>::check_block_length(&max_normal, len).unwrap();
441 assert_ok!(CheckWeight::<Test>::do_prepare(&max_normal, len, next_len));
442 assert_eq!(System::block_weight().total(), Weight::from_parts(768, 0));
443 let next_len = CheckWeight::<Test>::check_block_length(&rest_operational, len).unwrap();
444 assert_ok!(CheckWeight::<Test>::do_prepare(&rest_operational, len, next_len));
445 assert_eq!(block_weight_limit(), Weight::from_parts(1024, u64::MAX));
446 assert_eq!(System::block_weight().total(), block_weight_limit().set_proof_size(0));
447 assert_eq!(CheckWeight::<Test>::check_extrinsic_weight(&rest_operational, len), Ok(()));
449 });
450 }
451
452 #[test]
453 fn dispatch_order_does_not_effect_weight_logic() {
454 new_test_ext().execute_with(|| {
455 let max_normal =
457 DispatchInfo { call_weight: Weight::from_parts(753, 0), ..Default::default() };
458 let rest_operational = DispatchInfo {
459 call_weight: Weight::from_parts(246, 0),
460 class: DispatchClass::Operational,
461 ..Default::default()
462 };
463
464 let len = 0_usize;
465
466 let next_len = CheckWeight::<Test>::check_block_length(&rest_operational, len).unwrap();
467 assert_ok!(CheckWeight::<Test>::do_prepare(&rest_operational, len, next_len));
468 assert_eq!(System::block_weight().total(), Weight::from_parts(266, 0));
470 let next_len = CheckWeight::<Test>::check_block_length(&max_normal, len).unwrap();
471 assert_ok!(CheckWeight::<Test>::do_prepare(&max_normal, len, next_len));
472 assert_eq!(block_weight_limit(), Weight::from_parts(1024, u64::MAX));
473 assert_eq!(System::block_weight().total(), block_weight_limit().set_proof_size(0));
474 });
475 }
476
477 #[test]
478 fn operational_works_on_full_block() {
479 new_test_ext().execute_with(|| {
480 System::register_extra_weight_unchecked(Weight::MAX, DispatchClass::Mandatory);
482 let dispatch_normal = DispatchInfo {
483 call_weight: Weight::from_parts(251, 0),
484 class: DispatchClass::Normal,
485 ..Default::default()
486 };
487 let dispatch_operational = DispatchInfo {
488 call_weight: Weight::from_parts(246, 0),
489 class: DispatchClass::Operational,
490 ..Default::default()
491 };
492 let len = 0_usize;
493
494 let next_len = CheckWeight::<Test>::check_block_length(&dispatch_normal, len).unwrap();
495 assert_err!(
496 CheckWeight::<Test>::do_prepare(&dispatch_normal, len, next_len),
497 InvalidTransaction::ExhaustsResources
498 );
499 let next_len =
500 CheckWeight::<Test>::check_block_length(&dispatch_operational, len).unwrap();
501 assert_ok!(CheckWeight::<Test>::do_prepare(&dispatch_operational, len, next_len));
504 assert_err!(
506 CheckWeight::<Test>::do_prepare(&dispatch_operational, len, next_len),
507 InvalidTransaction::ExhaustsResources
508 );
509 assert_eq!(
511 CheckWeight::<Test>::check_extrinsic_weight(&dispatch_operational, len),
512 Ok(())
513 );
514 });
515 }
516
517 #[test]
518 fn signed_ext_check_weight_works_operational_tx() {
519 new_test_ext().execute_with(|| {
520 let normal =
521 DispatchInfo { call_weight: Weight::from_parts(100, 0), ..Default::default() };
522 let op = DispatchInfo {
523 call_weight: Weight::from_parts(100, 0),
524 extension_weight: Weight::zero(),
525 class: DispatchClass::Operational,
526 pays_fee: Pays::Yes,
527 };
528 let len = 0_usize;
529 let normal_limit = normal_weight_limit();
530
531 BlockWeight::<Test>::mutate(|current_weight| {
533 current_weight.set(normal_limit, DispatchClass::Normal)
534 });
535 assert_eq!(
537 CheckWeight::<Test>(PhantomData)
538 .validate_and_prepare(Some(1).into(), CALL, &normal, len, 0)
539 .unwrap_err(),
540 InvalidTransaction::ExhaustsResources.into()
541 );
542 assert_ok!(CheckWeight::<Test>(PhantomData).validate_and_prepare(
544 Some(1).into(),
545 CALL,
546 &op,
547 len,
548 0,
549 ));
550
551 let len = 100_usize;
553 AllExtrinsicsLen::<Test>::put(normal_length_limit());
554 assert_eq!(
555 CheckWeight::<Test>(PhantomData)
556 .validate_and_prepare(Some(1).into(), CALL, &normal, len, 0)
557 .unwrap_err(),
558 InvalidTransaction::ExhaustsResources.into()
559 );
560 assert_ok!(CheckWeight::<Test>(PhantomData).validate_and_prepare(
561 Some(1).into(),
562 CALL,
563 &op,
564 len,
565 0,
566 ));
567 })
568 }
569
570 #[test]
571 fn signed_ext_check_weight_block_size_works() {
572 new_test_ext().execute_with(|| {
573 let normal = DispatchInfo::default();
574 let normal_limit = normal_weight_limit().ref_time() as usize;
575 let reset_check_weight = |tx, s, f| {
576 AllExtrinsicsLen::<Test>::put(0);
577 let r = CheckWeight::<Test>(PhantomData).validate_and_prepare(
578 Some(1).into(),
579 CALL,
580 tx,
581 s,
582 0,
583 );
584 if f {
585 assert!(r.is_err())
586 } else {
587 assert!(r.is_ok())
588 }
589 };
590
591 reset_check_weight(&normal, normal_limit - 1, false);
592 reset_check_weight(&normal, normal_limit, false);
593 reset_check_weight(&normal, normal_limit + 1, true);
594
595 let op = DispatchInfo {
597 call_weight: Weight::zero(),
598 extension_weight: Weight::zero(),
599 class: DispatchClass::Operational,
600 pays_fee: Pays::Yes,
601 };
602 reset_check_weight(&op, normal_limit, false);
603 reset_check_weight(&op, normal_limit + 100, false);
604 reset_check_weight(&op, 1024, false);
605 reset_check_weight(&op, 1025, true);
606 })
607 }
608
609 #[test]
610 fn signed_ext_check_weight_works_normal_tx() {
611 new_test_ext().execute_with(|| {
612 let normal_limit = normal_weight_limit();
613 let small =
614 DispatchInfo { call_weight: Weight::from_parts(100, 0), ..Default::default() };
615 let base_extrinsic = block_weights().get(DispatchClass::Normal).base_extrinsic;
616 let medium =
617 DispatchInfo { call_weight: normal_limit - base_extrinsic, ..Default::default() };
618 let big = DispatchInfo {
619 call_weight: normal_limit - base_extrinsic + Weight::from_parts(1, 0),
620 ..Default::default()
621 };
622 let len = 0_usize;
623
624 let reset_check_weight = |i, f, s| {
625 BlockWeight::<Test>::mutate(|current_weight| {
626 current_weight.set(s, DispatchClass::Normal)
627 });
628 let r = CheckWeight::<Test>(PhantomData).validate_and_prepare(
629 Some(1).into(),
630 CALL,
631 i,
632 len,
633 0,
634 );
635 if f {
636 assert!(r.is_err())
637 } else {
638 assert!(r.is_ok())
639 }
640 };
641
642 reset_check_weight(&small, false, Weight::zero());
643 reset_check_weight(&medium, false, Weight::zero());
644 reset_check_weight(&big, true, Weight::from_parts(1, 0));
645 })
646 }
647
648 #[test]
649 fn signed_ext_check_weight_refund_works() {
650 new_test_ext().execute_with(|| {
651 let info =
653 DispatchInfo { call_weight: Weight::from_parts(512, 0), ..Default::default() };
654 let post_info = PostDispatchInfo {
655 actual_weight: Some(Weight::from_parts(128, 0)),
656 pays_fee: Default::default(),
657 };
658 let len = 0_usize;
659 let base_extrinsic = block_weights().get(DispatchClass::Normal).base_extrinsic;
660
661 BlockWeight::<Test>::mutate(|current_weight| {
663 current_weight.set(Weight::zero(), DispatchClass::Mandatory);
664 current_weight
665 .set(Weight::from_parts(256, 0) - base_extrinsic, DispatchClass::Normal);
666 });
667
668 let pre = CheckWeight::<Test>(PhantomData)
669 .validate_and_prepare(Some(1).into(), CALL, &info, len, 0)
670 .unwrap()
671 .0;
672 assert_eq!(
673 BlockWeight::<Test>::get().total(),
674 info.total_weight() + Weight::from_parts(256, 0)
675 );
676
677 assert_ok!(CheckWeight::<Test>::post_dispatch_details(
678 pre,
679 &info,
680 &post_info,
681 len,
682 &Ok(())
683 ));
684 assert_eq!(
685 BlockWeight::<Test>::get().total(),
686 post_info.actual_weight.unwrap() + Weight::from_parts(256, 0)
687 );
688 })
689 }
690
691 #[test]
692 fn signed_ext_check_weight_actual_weight_higher_than_max_is_capped() {
693 new_test_ext().execute_with(|| {
694 let info =
695 DispatchInfo { call_weight: Weight::from_parts(512, 0), ..Default::default() };
696 let post_info = PostDispatchInfo {
697 actual_weight: Some(Weight::from_parts(700, 0)),
698 pays_fee: Default::default(),
699 };
700 let len = 0_usize;
701
702 BlockWeight::<Test>::mutate(|current_weight| {
703 current_weight.set(Weight::zero(), DispatchClass::Mandatory);
704 current_weight.set(Weight::from_parts(128, 0), DispatchClass::Normal);
705 });
706
707 let pre = CheckWeight::<Test>(PhantomData)
708 .validate_and_prepare(Some(1).into(), CALL, &info, len, 0)
709 .unwrap()
710 .0;
711 assert_eq!(
712 BlockWeight::<Test>::get().total(),
713 info.total_weight() +
714 Weight::from_parts(128, 0) +
715 block_weights().get(DispatchClass::Normal).base_extrinsic,
716 );
717
718 assert_ok!(CheckWeight::<Test>::post_dispatch_details(
719 pre,
720 &info,
721 &post_info,
722 len,
723 &Ok(())
724 ));
725 assert_eq!(
726 BlockWeight::<Test>::get().total(),
727 info.total_weight() +
728 Weight::from_parts(128, 0) +
729 block_weights().get(DispatchClass::Normal).base_extrinsic,
730 );
731 })
732 }
733
734 #[test]
735 fn extrinsic_already_refunded_more_precisely() {
736 new_test_ext().execute_with(|| {
737 let info =
739 DispatchInfo { call_weight: Weight::from_parts(512, 0), ..Default::default() };
740 let post_info = PostDispatchInfo {
741 actual_weight: Some(Weight::from_parts(128, 0)),
742 pays_fee: Default::default(),
743 };
744 let prior_block_weight = Weight::from_parts(64, 0);
745 let accurate_refund = Weight::from_parts(510, 0);
746 let len = 0_usize;
747 let base_extrinsic = block_weights().get(DispatchClass::Normal).base_extrinsic;
748
749 BlockWeight::<Test>::mutate(|current_weight| {
751 current_weight.set(Weight::zero(), DispatchClass::Mandatory);
752 current_weight.set(prior_block_weight, DispatchClass::Normal);
753 });
754
755 let pre = CheckWeight::<Test>(PhantomData)
757 .validate_and_prepare(Some(1).into(), CALL, &info, len, 0)
758 .unwrap()
759 .0;
760
761 assert_eq!(
762 BlockWeight::<Test>::get().total(),
763 info.total_weight() + prior_block_weight + base_extrinsic
764 );
765
766 BlockWeight::<Test>::mutate(|current_weight| {
768 current_weight.reduce(accurate_refund, DispatchClass::Normal);
769 });
770 crate::ExtrinsicWeightReclaimed::<Test>::put(accurate_refund);
771
772 assert_ok!(CheckWeight::<Test>::post_dispatch_details(
774 pre,
775 &info,
776 &post_info,
777 len,
778 &Ok(())
779 ));
780
781 assert_eq!(crate::ExtrinsicWeightReclaimed::<Test>::get(), accurate_refund);
783 assert_eq!(
784 BlockWeight::<Test>::get().total(),
785 info.total_weight() - accurate_refund + prior_block_weight + base_extrinsic
786 );
787 })
788 }
789
790 #[test]
791 fn extrinsic_already_refunded_less_precisely() {
792 new_test_ext().execute_with(|| {
793 let info =
795 DispatchInfo { call_weight: Weight::from_parts(512, 0), ..Default::default() };
796 let post_info = PostDispatchInfo {
797 actual_weight: Some(Weight::from_parts(128, 0)),
798 pays_fee: Default::default(),
799 };
800 let prior_block_weight = Weight::from_parts(64, 0);
801 let inaccurate_refund = Weight::from_parts(110, 0);
802 let len = 0_usize;
803 let base_extrinsic = block_weights().get(DispatchClass::Normal).base_extrinsic;
804
805 BlockWeight::<Test>::mutate(|current_weight| {
807 current_weight.set(Weight::zero(), DispatchClass::Mandatory);
808 current_weight.set(prior_block_weight, DispatchClass::Normal);
809 });
810
811 let pre = CheckWeight::<Test>(PhantomData)
813 .validate_and_prepare(Some(1).into(), CALL, &info, len, 0)
814 .unwrap()
815 .0;
816
817 let expected = info.total_weight() + prior_block_weight + base_extrinsic;
818 assert_eq!(expected, BlockWeight::<Test>::get().total());
819 assert_eq!(
820 RuntimeBlockWeights::get().max_block - expected,
821 System::remaining_block_weight().remaining()
822 );
823
824 BlockWeight::<Test>::mutate(|current_weight| {
826 current_weight.reduce(inaccurate_refund, DispatchClass::Normal);
827 });
828 crate::ExtrinsicWeightReclaimed::<Test>::put(inaccurate_refund);
829
830 assert_ok!(CheckWeight::<Test>::post_dispatch_details(
832 pre,
833 &info,
834 &post_info,
835 len,
836 &Ok(())
837 ));
838
839 assert_eq!(
841 crate::ExtrinsicWeightReclaimed::<Test>::get(),
842 post_info.calc_unspent(&info)
843 );
844 let expected = post_info.actual_weight.unwrap() + prior_block_weight + base_extrinsic;
845 assert_eq!(expected, BlockWeight::<Test>::get().total());
846 assert_eq!(
847 RuntimeBlockWeights::get().max_block - expected,
848 System::remaining_block_weight().remaining()
849 );
850 })
851 }
852
853 #[test]
854 fn zero_weight_extrinsic_still_has_base_weight() {
855 new_test_ext().execute_with(|| {
856 let weights = block_weights();
857 let free = DispatchInfo { call_weight: Weight::zero(), ..Default::default() };
858 let len = 0_usize;
859
860 assert_eq!(System::block_weight().total(), weights.base_block);
862 assert_ok!(CheckWeight::<Test>(PhantomData).validate_and_prepare(
863 Some(1).into(),
864 CALL,
865 &free,
866 len,
867 0,
868 ));
869 assert_eq!(
870 System::block_weight().total(),
871 weights.get(DispatchClass::Normal).base_extrinsic + weights.base_block
872 );
873 })
874 }
875
876 #[test]
877 fn normal_and_mandatory_tracked_separately() {
878 new_test_ext().execute_with(|| {
879 let max_normal =
883 DispatchInfo { call_weight: Weight::from_parts(753, 0), ..Default::default() };
884 let mandatory = DispatchInfo {
885 call_weight: Weight::from_parts(1019, 0),
886 class: DispatchClass::Mandatory,
887 ..Default::default()
888 };
889
890 let len = 0_usize;
891
892 let next_len = CheckWeight::<Test>::check_block_length(&max_normal, len).unwrap();
893 assert_ok!(CheckWeight::<Test>::do_prepare(&max_normal, len, next_len));
894 assert_eq!(System::block_weight().total(), Weight::from_parts(768, 0));
895 let next_len = CheckWeight::<Test>::check_block_length(&mandatory, len).unwrap();
896 assert_ok!(CheckWeight::<Test>::do_prepare(&mandatory, len, next_len));
897 assert_eq!(block_weight_limit(), Weight::from_parts(1024, u64::MAX));
898 assert_eq!(System::block_weight().total(), Weight::from_parts(1024 + 768, 0));
899 assert_eq!(CheckWeight::<Test>::check_extrinsic_weight(&mandatory, len), Ok(()));
900 });
901 }
902
903 #[test]
904 fn no_max_total_should_still_be_limited_by_max_block() {
905 let maximum_weight = BlockWeights::builder()
907 .base_block(Weight::zero())
908 .for_class(DispatchClass::non_mandatory(), |w| {
909 w.base_extrinsic = Weight::zero();
910 w.max_total = Some(Weight::from_parts(20, u64::MAX));
911 })
912 .for_class(DispatchClass::Mandatory, |w| {
913 w.base_extrinsic = Weight::zero();
914 w.reserved = Some(Weight::from_parts(5, u64::MAX));
915 w.max_total = None;
916 })
917 .build_or_panic();
918 let all_weight = crate::ConsumedWeight::new(|class| match class {
919 DispatchClass::Normal => Weight::from_parts(10, 0),
920 DispatchClass::Operational => Weight::from_parts(10, 0),
921 DispatchClass::Mandatory => Weight::zero(),
922 });
923 assert_eq!(maximum_weight.max_block, all_weight.total().set_proof_size(u64::MAX));
924
925 let mandatory1 = DispatchInfo {
927 call_weight: Weight::from_parts(5, 0),
928 class: DispatchClass::Mandatory,
929 ..Default::default()
930 };
931 let mandatory2 = DispatchInfo {
933 call_weight: Weight::from_parts(6, 0),
934 class: DispatchClass::Mandatory,
935 ..Default::default()
936 };
937
938 assert_ok!(calculate_consumed_weight::<<Test as Config>::RuntimeCall>(
940 &maximum_weight,
941 all_weight.clone(),
942 &mandatory1,
943 0
944 ));
945 assert_err!(
946 calculate_consumed_weight::<<Test as Config>::RuntimeCall>(
947 &maximum_weight,
948 all_weight,
949 &mandatory2,
950 0
951 ),
952 InvalidTransaction::ExhaustsResources
953 );
954 }
955
956 #[test]
957 fn check_extrinsic_proof_weight_includes_length() {
958 new_test_ext().execute_with(|| {
959 let weights = block_weights();
961 let max_extrinsic = weights.get(DispatchClass::Normal).max_extrinsic.unwrap();
962
963 let max_proof_size = max_extrinsic.proof_size() as usize;
964 let info = DispatchInfo {
966 call_weight: max_extrinsic.set_proof_size(0),
967 class: DispatchClass::Normal,
968 ..Default::default()
969 };
970
971 assert_ok!(CheckWeight::<Test>::check_extrinsic_weight(&info, 0));
973
974 assert_ok!(CheckWeight::<Test>::check_extrinsic_weight(&info, 100));
976
977 assert_ok!(CheckWeight::<Test>::check_extrinsic_weight(&info, max_proof_size));
979
980 assert_err!(
982 CheckWeight::<Test>::check_extrinsic_weight(&info, max_proof_size + 1),
983 InvalidTransaction::ExhaustsResources
984 );
985
986 let info_at_limit = DispatchInfo {
988 call_weight: max_extrinsic,
989 class: DispatchClass::Normal,
990 ..Default::default()
991 };
992
993 assert_ok!(CheckWeight::<Test>::check_extrinsic_weight(&info_at_limit, 0));
995
996 assert_err!(
998 CheckWeight::<Test>::check_extrinsic_weight(&info_at_limit, 1),
999 InvalidTransaction::ExhaustsResources
1000 );
1001
1002 let info_zero = DispatchInfo {
1004 call_weight: Weight::zero(),
1005 class: DispatchClass::Normal,
1006 ..Default::default()
1007 };
1008 let large_len = usize::MAX;
1010
1011 let result = CheckWeight::<Test>::check_extrinsic_weight(&info_zero, large_len);
1013 assert_err!(result, InvalidTransaction::ExhaustsResources);
1015
1016 let info_with_minimal_proof_size = DispatchInfo {
1018 call_weight: Weight::from_parts(0, 10),
1019 class: DispatchClass::Normal,
1020 ..Default::default()
1021 };
1022
1023 let result = CheckWeight::<Test>::check_extrinsic_weight(
1025 &info_with_minimal_proof_size,
1026 large_len,
1027 );
1028 assert_err!(result, InvalidTransaction::ExhaustsResources);
1030 });
1031 }
1032
1033 #[test]
1034 fn proof_size_includes_length() {
1035 let maximum_weight = BlockWeights::builder()
1036 .base_block(Weight::zero())
1037 .for_class(DispatchClass::non_mandatory(), |w| {
1038 w.base_extrinsic = Weight::zero();
1039 w.max_total = Some(Weight::from_parts(20, 1000));
1040 })
1041 .for_class(DispatchClass::Mandatory, |w| {
1042 w.base_extrinsic = Weight::zero();
1043 w.max_total = Some(Weight::from_parts(20, 1000));
1044 })
1045 .build_or_panic();
1046 let all_weight = crate::ConsumedWeight::new(|class| match class {
1047 DispatchClass::Normal => Weight::from_parts(5, 0),
1048 DispatchClass::Operational => Weight::from_parts(5, 0),
1049 DispatchClass::Mandatory => Weight::from_parts(0, 0),
1050 });
1051
1052 let normal = DispatchInfo {
1053 call_weight: Weight::from_parts(5, 0),
1054 class: DispatchClass::Normal,
1055 ..Default::default()
1056 };
1057
1058 let mandatory = DispatchInfo {
1059 call_weight: Weight::from_parts(5, 0),
1060 class: DispatchClass::Mandatory,
1061 ..Default::default()
1062 };
1063
1064 let consumed = calculate_consumed_weight::<<Test as Config>::RuntimeCall>(
1066 &maximum_weight,
1067 all_weight.clone(),
1068 &normal,
1069 0,
1070 )
1071 .unwrap();
1072
1073 assert_eq!(consumed.total().saturating_sub(all_weight.total()), normal.total_weight());
1074
1075 let consumed = calculate_consumed_weight::<<Test as Config>::RuntimeCall>(
1076 &maximum_weight,
1077 all_weight.clone(),
1078 &mandatory,
1079 0,
1080 )
1081 .unwrap();
1082 assert_eq!(consumed.total().saturating_sub(all_weight.total()), mandatory.total_weight());
1083
1084 let consumed = calculate_consumed_weight::<<Test as Config>::RuntimeCall>(
1086 &maximum_weight,
1087 all_weight.clone(),
1088 &normal,
1089 100,
1090 )
1091 .unwrap();
1092 assert_eq!(
1094 consumed.total().saturating_sub(all_weight.total()),
1095 normal.total_weight().add_proof_size(100)
1096 );
1097
1098 let consumed = calculate_consumed_weight::<<Test as Config>::RuntimeCall>(
1099 &maximum_weight,
1100 all_weight.clone(),
1101 &mandatory,
1102 100,
1103 )
1104 .unwrap();
1105 assert_eq!(
1107 consumed.total().saturating_sub(all_weight.total()),
1108 mandatory.total_weight().add_proof_size(100)
1109 );
1110
1111 let consumed = calculate_consumed_weight::<<Test as Config>::RuntimeCall>(
1113 &maximum_weight,
1114 all_weight.clone(),
1115 &normal,
1116 2000,
1117 );
1118 assert_eq!(consumed, Err(InvalidTransaction::ExhaustsResources.into()));
1120
1121 let consumed = calculate_consumed_weight::<<Test as Config>::RuntimeCall>(
1123 &maximum_weight,
1124 all_weight.clone(),
1125 &mandatory,
1126 2000,
1127 );
1128 assert_eq!(consumed, Err(InvalidTransaction::ExhaustsResources.into()));
1130 }
1131}