@@ -50,10 +50,12 @@ where
5050 /// with given `DispatchClass` can have.
5151 fn check_extrinsic_weight (
5252 info : & DispatchInfoOf < T :: RuntimeCall > ,
53+ len : usize ,
5354 ) -> Result < ( ) , TransactionValidityError > {
5455 let max = T :: BlockWeights :: get ( ) . get ( info. class ) . max_extrinsic ;
56+ let total_weight_including_length = info. total_weight ( ) . add_proof_size ( len as u64 ) ;
5557 match max {
56- Some ( max) if info . total_weight ( ) . any_gt ( max) => {
58+ Some ( max) if total_weight_including_length . any_gt ( max) => {
5759 log:: debug!(
5860 target: LOG_TARGET ,
5961 "Extrinsic {} is greater than the max extrinsic {}" ,
@@ -111,7 +113,7 @@ where
111113 // during validation we skip block limit check. Since the `validate_transaction`
112114 // call runs on an empty block anyway, by this we prevent `on_initialize` weight
113115 // consumption from causing false negatives.
114- Self :: check_extrinsic_weight ( info) ?;
116+ Self :: check_extrinsic_weight ( info, len ) ?;
115117
116118 Ok ( ( Default :: default ( ) , next_len) )
117119 }
@@ -442,7 +444,7 @@ mod tests {
442444 assert_eq ! ( block_weight_limit( ) , Weight :: from_parts( 1024 , u64 :: MAX ) ) ;
443445 assert_eq ! ( System :: block_weight( ) . total( ) , block_weight_limit( ) . set_proof_size( 0 ) ) ;
444446 // Checking single extrinsic should not take current block weight into account.
445- assert_eq ! ( CheckWeight :: <Test >:: check_extrinsic_weight( & rest_operational) , Ok ( ( ) ) ) ;
447+ assert_eq ! ( CheckWeight :: <Test >:: check_extrinsic_weight( & rest_operational, len ) , Ok ( ( ) ) ) ;
446448 } ) ;
447449 }
448450
@@ -504,7 +506,10 @@ mod tests {
504506 InvalidTransaction :: ExhaustsResources
505507 ) ;
506508 // Even with full block, validity of single transaction should be correct.
507- assert_eq ! ( CheckWeight :: <Test >:: check_extrinsic_weight( & dispatch_operational) , Ok ( ( ) ) ) ;
509+ assert_eq ! (
510+ CheckWeight :: <Test >:: check_extrinsic_weight( & dispatch_operational, len) ,
511+ Ok ( ( ) )
512+ ) ;
508513 } ) ;
509514 }
510515
@@ -890,7 +895,7 @@ mod tests {
890895 assert_ok ! ( CheckWeight :: <Test >:: do_prepare( & mandatory, len, next_len) ) ;
891896 assert_eq ! ( block_weight_limit( ) , Weight :: from_parts( 1024 , u64 :: MAX ) ) ;
892897 assert_eq ! ( System :: block_weight( ) . total( ) , Weight :: from_parts( 1024 + 768 , 0 ) ) ;
893- assert_eq ! ( CheckWeight :: <Test >:: check_extrinsic_weight( & mandatory) , Ok ( ( ) ) ) ;
898+ assert_eq ! ( CheckWeight :: <Test >:: check_extrinsic_weight( & mandatory, len ) , Ok ( ( ) ) ) ;
894899 } ) ;
895900 }
896901
@@ -947,6 +952,66 @@ mod tests {
947952 ) ;
948953 }
949954
955+ #[ test]
956+ fn check_extrinsic_proof_weight_includes_length ( ) {
957+ new_test_ext ( ) . execute_with ( || {
958+ // Test that check_extrinsic_weight properly includes length in proof size check
959+ let weights = block_weights ( ) ;
960+ let max_extrinsic = weights. get ( DispatchClass :: Normal ) . max_extrinsic . unwrap ( ) ;
961+
962+ let max_proof_size = max_extrinsic. proof_size ( ) as usize ;
963+ // Extrinsic weight that fits without length
964+ let info = DispatchInfo {
965+ call_weight : max_extrinsic. set_proof_size ( 0 ) ,
966+ class : DispatchClass :: Normal ,
967+ ..Default :: default ( )
968+ } ;
969+
970+ // With zero length, should succeed
971+ assert_ok ! ( CheckWeight :: <Test >:: check_extrinsic_weight( & info, 0 ) ) ;
972+
973+ // With small length, should succeed
974+ assert_ok ! ( CheckWeight :: <Test >:: check_extrinsic_weight( & info, 100 ) ) ;
975+
976+ // With small length, should succeed
977+ assert_ok ! ( CheckWeight :: <Test >:: check_extrinsic_weight( & info, max_proof_size) ) ;
978+
979+ // One byte above limit, should fail
980+ assert_err ! (
981+ CheckWeight :: <Test >:: check_extrinsic_weight( & info, max_proof_size + 1 ) ,
982+ InvalidTransaction :: ExhaustsResources
983+ ) ;
984+
985+ // Now test an extrinsic that's at the limit for proof size
986+ let info_at_limit = DispatchInfo {
987+ call_weight : max_extrinsic,
988+ class : DispatchClass :: Normal ,
989+ ..Default :: default ( )
990+ } ;
991+
992+ // At limit with zero length should succeed
993+ assert_ok ! ( CheckWeight :: <Test >:: check_extrinsic_weight( & info_at_limit, 0 ) ) ;
994+
995+ // Over limit when length is added should fail
996+ assert_err ! (
997+ CheckWeight :: <Test >:: check_extrinsic_weight( & info_at_limit, 1 ) ,
998+ InvalidTransaction :: ExhaustsResources
999+ ) ;
1000+
1001+ // Test with very large length (near usize::MAX on 32-bit systems)
1002+ let info_zero = DispatchInfo {
1003+ call_weight : Weight :: zero ( ) ,
1004+ class : DispatchClass :: Normal ,
1005+ ..Default :: default ( )
1006+ } ;
1007+ // Should handle large lengths gracefully via saturating conversion
1008+ let large_len = usize:: MAX ;
1009+ let result = CheckWeight :: < Test > :: check_extrinsic_weight ( & info_zero, large_len) ;
1010+ // This should fail because u64::MAX proof size exceeds limits
1011+ assert_err ! ( result, InvalidTransaction :: ExhaustsResources ) ;
1012+ } ) ;
1013+ }
1014+
9501015 #[ test]
9511016 fn proof_size_includes_length ( ) {
9521017 let maximum_weight = BlockWeights :: builder ( )
0 commit comments