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