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