Skip to content

Commit 6d3a6d8

Browse files
skunertacatangiu
andauthored
CheckWeight SE: Check for extrinsic length + proof size combined (#4326)
Currently the `CheckWeight` `SignedExtension` was tracking the size of the proof and the extrinsic length separately. But in reality we need one more check that ensures we don't hit the PoV limit with both combined. The rest of the logic remains unchanged. One scenario where the changes make a difference is when we enter this branch: https://github.com/paritytech/polkadot-sdk/blob/f34d8e3cf033e2a22a41b505c437972a5dc83d78/substrate/frame/system/src/extensions/check_weight.rs#L185-L198 This was previously allowing to some extrinsics that is exceeding the block limit but are withing the reserved area of `BlockWeights`. This will now be caught by the later check I introduced. I think the new behaviour makes sense, since the proof size dimension is designed for parachains and they don't want to go over the limit and get rejected. In the long run we should maybe get rid of `RuntimeBlockLength` alltogether, however that would require a deprecation process and can come at a later point. --------- Co-authored-by: Adrian Catangiu <[email protected]>
1 parent f4b73bd commit 6d3a6d8

File tree

2 files changed

+124
-17
lines changed

2 files changed

+124
-17
lines changed

prdoc/pr_4326.prdoc

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
# Schema: Polkadot SDK PRDoc Schema (prdoc) v1.0.0
2+
# See doc at https://raw.githubusercontent.com/paritytech/polkadot-sdk/master/prdoc/schema_user.json
3+
4+
title: CheckWeight checks for combined extrinsic length and proof size
5+
6+
doc:
7+
- audience: Runtime Dev
8+
description: |
9+
The `CheckWeight` `SignedExtension` will now perform an additional check. The extension was verifying the extrinsic length and
10+
weight limits individually. However, the proof size dimension of the weight and extrinsic length together are bound by the PoV size limit.
11+
The `CheckWeight` extension will now check that the combined size of the proof and the extrinsic lengths will not
12+
exceed the PoV size limit.
13+
14+
crates:
15+
- name: frame-system
16+
bump: minor

substrate/frame/system/src/extensions/check_weight.rs

Lines changed: 108 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -64,17 +64,6 @@ where
6464
}
6565
}
6666

67-
/// Checks if the current extrinsic can fit into the block with respect to block weight limits.
68-
///
69-
/// Upon successes, it returns the new block weight as a `Result`.
70-
fn check_block_weight(
71-
info: &DispatchInfoOf<T::RuntimeCall>,
72-
) -> Result<crate::ConsumedWeight, TransactionValidityError> {
73-
let maximum_weight = T::BlockWeights::get();
74-
let all_weight = Pallet::<T>::block_weight();
75-
calculate_consumed_weight::<T::RuntimeCall>(maximum_weight, all_weight, info)
76-
}
77-
7867
/// Checks if the current extrinsic can fit into the block with respect to block length limits.
7968
///
8069
/// Upon successes, it returns the new block length as a `Result`.
@@ -113,7 +102,12 @@ where
113102
len: usize,
114103
) -> Result<(), TransactionValidityError> {
115104
let next_len = Self::check_block_length(info, len)?;
116-
let next_weight = Self::check_block_weight(info)?;
105+
106+
let all_weight = Pallet::<T>::block_weight();
107+
let maximum_weight = T::BlockWeights::get();
108+
let next_weight =
109+
calculate_consumed_weight::<T::RuntimeCall>(&maximum_weight, all_weight, info)?;
110+
check_combined_proof_size(&maximum_weight, next_len, &next_weight)?;
117111
Self::check_extrinsic_weight(info)?;
118112

119113
crate::AllExtrinsicsLen::<T>::put(next_len);
@@ -136,8 +130,32 @@ where
136130
}
137131
}
138132

133+
/// Check that the combined extrinsic length and proof size together do not exceed the PoV limit.
134+
pub fn check_combined_proof_size(
135+
maximum_weight: &BlockWeights,
136+
next_len: u32,
137+
next_weight: &crate::ConsumedWeight,
138+
) -> Result<(), TransactionValidityError> {
139+
// This extra check ensures that the extrinsic length does not push the
140+
// PoV over the limit.
141+
let total_pov_size = next_weight.total().proof_size().saturating_add(next_len as u64);
142+
if total_pov_size > maximum_weight.max_block.proof_size() {
143+
log::debug!(
144+
target: LOG_TARGET,
145+
"Extrinsic exceeds total pov size: {}kb, limit: {}kb",
146+
total_pov_size as f64/1024.0,
147+
maximum_weight.max_block.proof_size() as f64/1024.0
148+
);
149+
return Err(InvalidTransaction::ExhaustsResources.into())
150+
}
151+
Ok(())
152+
}
153+
154+
/// Checks if the current extrinsic can fit into the block with respect to block weight limits.
155+
///
156+
/// Upon successes, it returns the new block weight as a `Result`.
139157
pub fn calculate_consumed_weight<Call>(
140-
maximum_weight: BlockWeights,
158+
maximum_weight: &BlockWeights,
141159
mut all_weight: crate::ConsumedWeight,
142160
info: &DispatchInfoOf<Call>,
143161
) -> Result<crate::ConsumedWeight, TransactionValidityError>
@@ -742,17 +760,90 @@ mod tests {
742760

743761
// when
744762
assert_ok!(calculate_consumed_weight::<<Test as Config>::RuntimeCall>(
745-
maximum_weight.clone(),
763+
&maximum_weight,
746764
all_weight.clone(),
747-
&mandatory1
765+
&mandatory1,
748766
));
749767
assert_err!(
750768
calculate_consumed_weight::<<Test as Config>::RuntimeCall>(
751-
maximum_weight,
769+
&maximum_weight,
752770
all_weight,
753-
&mandatory2
771+
&mandatory2,
754772
),
755773
InvalidTransaction::ExhaustsResources
756774
);
757775
}
776+
777+
#[test]
778+
fn maximum_proof_size_includes_length() {
779+
let maximum_weight = BlockWeights::builder()
780+
.base_block(Weight::zero())
781+
.for_class(DispatchClass::non_mandatory(), |w| {
782+
w.base_extrinsic = Weight::zero();
783+
w.max_total = Some(Weight::from_parts(20, 10));
784+
})
785+
.for_class(DispatchClass::Mandatory, |w| {
786+
w.base_extrinsic = Weight::zero();
787+
w.reserved = Some(Weight::from_parts(5, 10));
788+
w.max_total = None;
789+
})
790+
.build_or_panic();
791+
792+
assert_eq!(maximum_weight.max_block, Weight::from_parts(20, 10));
793+
794+
// We have 10 reftime and 5 proof size left over.
795+
let next_weight = crate::ConsumedWeight::new(|class| match class {
796+
DispatchClass::Normal => Weight::from_parts(10, 5),
797+
DispatchClass::Operational => Weight::from_parts(0, 0),
798+
DispatchClass::Mandatory => Weight::zero(),
799+
});
800+
801+
// Simple checks for the length
802+
assert_ok!(check_combined_proof_size(&maximum_weight, 0, &next_weight));
803+
assert_ok!(check_combined_proof_size(&maximum_weight, 5, &next_weight));
804+
assert_err!(
805+
check_combined_proof_size(&maximum_weight, 6, &next_weight),
806+
InvalidTransaction::ExhaustsResources
807+
);
808+
809+
// We have 10 reftime and 0 proof size left over.
810+
let next_weight = crate::ConsumedWeight::new(|class| match class {
811+
DispatchClass::Normal => Weight::from_parts(10, 10),
812+
DispatchClass::Operational => Weight::from_parts(0, 0),
813+
DispatchClass::Mandatory => Weight::zero(),
814+
});
815+
assert_ok!(check_combined_proof_size(&maximum_weight, 0, &next_weight));
816+
assert_err!(
817+
check_combined_proof_size(&maximum_weight, 1, &next_weight),
818+
InvalidTransaction::ExhaustsResources
819+
);
820+
821+
// We have 10 reftime and 2 proof size left over.
822+
// Used weight is spread across dispatch classes this time.
823+
let next_weight = crate::ConsumedWeight::new(|class| match class {
824+
DispatchClass::Normal => Weight::from_parts(10, 5),
825+
DispatchClass::Operational => Weight::from_parts(0, 3),
826+
DispatchClass::Mandatory => Weight::zero(),
827+
});
828+
assert_ok!(check_combined_proof_size(&maximum_weight, 0, &next_weight));
829+
assert_ok!(check_combined_proof_size(&maximum_weight, 2, &next_weight));
830+
assert_err!(
831+
check_combined_proof_size(&maximum_weight, 3, &next_weight),
832+
InvalidTransaction::ExhaustsResources
833+
);
834+
835+
// Ref time is over the limit. Should not happen, but we should make sure that it is
836+
// ignored.
837+
let next_weight = crate::ConsumedWeight::new(|class| match class {
838+
DispatchClass::Normal => Weight::from_parts(30, 5),
839+
DispatchClass::Operational => Weight::from_parts(0, 0),
840+
DispatchClass::Mandatory => Weight::zero(),
841+
});
842+
assert_ok!(check_combined_proof_size(&maximum_weight, 0, &next_weight));
843+
assert_ok!(check_combined_proof_size(&maximum_weight, 5, &next_weight));
844+
assert_err!(
845+
check_combined_proof_size(&maximum_weight, 6, &next_weight),
846+
InvalidTransaction::ExhaustsResources
847+
);
848+
}
758849
}

0 commit comments

Comments
 (0)