Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

check-weight: Disable total pov size check for mandatory extrinsics #4571

Merged
merged 6 commits into from
May 27, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 19 additions & 0 deletions prdoc/pr_4571.prdoc
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# Schema: Polkadot SDK PRDoc Schema (prdoc) v1.0.0
# See doc at https://raw.githubusercontent.com/paritytech/polkadot-sdk/master/prdoc/schema_user.json

title: Ignore mandatory extrinsics in total PoV size check

doc:
- audience: Runtime Dev
description: |
The `CheckWeight` extension is checking that extrinsic length and used storage proof
weight together do not exceed the PoV size limit. This lead to problems when
the PoV size was already reached before mandatory extrinsics were applied.The `CheckWeight`
extension will now allow extrinsics of `DispatchClass::Mandatory` to be applied even if
the limit is reached.

crates:
- name: frame-system
bump: minor
- name: polkadot-sdk
bump: minor
130 changes: 110 additions & 20 deletions substrate/frame/system/src/extensions/check_weight.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.

use crate::{limits::BlockWeights, Config, Pallet, LOG_TARGET};
use crate::{limits::BlockWeights, Config, DispatchClass, Pallet, LOG_TARGET};
use codec::{Decode, Encode};
use frame_support::{
dispatch::{DispatchInfo, PostDispatchInfo},
Expand Down Expand Up @@ -107,7 +107,7 @@ where
let maximum_weight = T::BlockWeights::get();
let next_weight =
calculate_consumed_weight::<T::RuntimeCall>(&maximum_weight, all_weight, info)?;
check_combined_proof_size(&maximum_weight, next_len, &next_weight)?;
check_combined_proof_size::<T::RuntimeCall>(info, &maximum_weight, next_len, &next_weight)?;
Self::check_extrinsic_weight(info)?;

crate::AllExtrinsicsLen::<T>::put(next_len);
Expand All @@ -131,22 +131,31 @@ where
}

/// Check that the combined extrinsic length and proof size together do not exceed the PoV limit.
pub fn check_combined_proof_size(
pub fn check_combined_proof_size<Call>(
info: &DispatchInfoOf<Call>,
maximum_weight: &BlockWeights,
next_len: u32,
next_weight: &crate::ConsumedWeight,
) -> Result<(), TransactionValidityError> {
) -> Result<(), TransactionValidityError>
where
Call: Dispatchable<Info = DispatchInfo, PostInfo = PostDispatchInfo>,
{
// This extra check ensures that the extrinsic length does not push the
// PoV over the limit.
let total_pov_size = next_weight.total().proof_size().saturating_add(next_len as u64);
if total_pov_size > maximum_weight.max_block.proof_size() {
log::debug!(
target: LOG_TARGET,
"Extrinsic exceeds total pov size: {}kb, limit: {}kb",
"Extrinsic exceeds total pov size. Still including if mandatory. size: {}kb, limit: {}kb, is_mandatory: {}",
total_pov_size as f64/1024.0,
maximum_weight.max_block.proof_size() as f64/1024.0
maximum_weight.max_block.proof_size() as f64/1024.0,
info.class == DispatchClass::Mandatory
);
return Err(InvalidTransaction::ExhaustsResources.into())
return match info.class {
// Allow mandatory extrinsics
DispatchClass::Mandatory => Ok(()),
_ => Err(InvalidTransaction::ExhaustsResources.into()),
};
}
Ok(())
}
Expand Down Expand Up @@ -190,7 +199,7 @@ where
"Exceeded the per-class allowance.",
);

return Err(InvalidTransaction::ExhaustsResources.into())
return Err(InvalidTransaction::ExhaustsResources.into());
},
// There is no `max_total` limit (`None`),
// or we are below the limit.
Expand All @@ -208,7 +217,7 @@ where
"Total block weight is exceeded.",
);

return Err(InvalidTransaction::ExhaustsResources.into())
return Err(InvalidTransaction::ExhaustsResources.into());
},
// There is either no limit in reserved pool (`None`),
// or we are below the limit.
Expand Down Expand Up @@ -791,6 +800,8 @@ mod tests {

assert_eq!(maximum_weight.max_block, Weight::from_parts(20, 10));

let info = DispatchInfo { class: DispatchClass::Normal, ..Default::default() };
let mandatory = DispatchInfo { class: DispatchClass::Mandatory, ..Default::default() };
// We have 10 reftime and 5 proof size left over.
let next_weight = crate::ConsumedWeight::new(|class| match class {
DispatchClass::Normal => Weight::from_parts(10, 5),
Expand All @@ -799,24 +810,61 @@ mod tests {
});

// Simple checks for the length
assert_ok!(check_combined_proof_size(&maximum_weight, 0, &next_weight));
assert_ok!(check_combined_proof_size(&maximum_weight, 5, &next_weight));
assert_ok!(check_combined_proof_size::<<Test as Config>::RuntimeCall>(
&info,
&maximum_weight,
0,
&next_weight
));
assert_ok!(check_combined_proof_size::<<Test as Config>::RuntimeCall>(
&info,
&maximum_weight,
5,
&next_weight
));
assert_err!(
check_combined_proof_size(&maximum_weight, 6, &next_weight),
check_combined_proof_size::<<Test as Config>::RuntimeCall>(
&info,
&maximum_weight,
6,
&next_weight
),
InvalidTransaction::ExhaustsResources
);
assert_ok!(check_combined_proof_size::<<Test as Config>::RuntimeCall>(
&mandatory,
&maximum_weight,
6,
&next_weight
));

// We have 10 reftime and 0 proof size left over.
let next_weight = crate::ConsumedWeight::new(|class| match class {
DispatchClass::Normal => Weight::from_parts(10, 10),
DispatchClass::Operational => Weight::from_parts(0, 0),
DispatchClass::Mandatory => Weight::zero(),
});
assert_ok!(check_combined_proof_size(&maximum_weight, 0, &next_weight));
assert_ok!(check_combined_proof_size::<<Test as Config>::RuntimeCall>(
&info,
&maximum_weight,
0,
&next_weight
));
assert_err!(
check_combined_proof_size(&maximum_weight, 1, &next_weight),
check_combined_proof_size::<<Test as Config>::RuntimeCall>(
&info,
&maximum_weight,
1,
&next_weight
),
InvalidTransaction::ExhaustsResources
);
assert_ok!(check_combined_proof_size::<<Test as Config>::RuntimeCall>(
&mandatory,
&maximum_weight,
1,
&next_weight
));

// We have 10 reftime and 2 proof size left over.
// Used weight is spread across dispatch classes this time.
Expand All @@ -825,12 +873,33 @@ mod tests {
DispatchClass::Operational => Weight::from_parts(0, 3),
DispatchClass::Mandatory => Weight::zero(),
});
assert_ok!(check_combined_proof_size(&maximum_weight, 0, &next_weight));
assert_ok!(check_combined_proof_size(&maximum_weight, 2, &next_weight));
assert_ok!(check_combined_proof_size::<<Test as Config>::RuntimeCall>(
&info,
&maximum_weight,
0,
&next_weight
));
assert_ok!(check_combined_proof_size::<<Test as Config>::RuntimeCall>(
&info,
&maximum_weight,
2,
&next_weight
));
assert_err!(
check_combined_proof_size(&maximum_weight, 3, &next_weight),
check_combined_proof_size::<<Test as Config>::RuntimeCall>(
&info,
&maximum_weight,
3,
&next_weight
),
InvalidTransaction::ExhaustsResources
);
assert_ok!(check_combined_proof_size::<<Test as Config>::RuntimeCall>(
&mandatory,
&maximum_weight,
3,
&next_weight
));

// Ref time is over the limit. Should not happen, but we should make sure that it is
// ignored.
Expand All @@ -839,11 +908,32 @@ mod tests {
DispatchClass::Operational => Weight::from_parts(0, 0),
DispatchClass::Mandatory => Weight::zero(),
});
assert_ok!(check_combined_proof_size(&maximum_weight, 0, &next_weight));
assert_ok!(check_combined_proof_size(&maximum_weight, 5, &next_weight));
assert_ok!(check_combined_proof_size::<<Test as Config>::RuntimeCall>(
&info,
&maximum_weight,
0,
&next_weight
));
assert_ok!(check_combined_proof_size::<<Test as Config>::RuntimeCall>(
&info,
&maximum_weight,
5,
&next_weight
));
assert_err!(
check_combined_proof_size(&maximum_weight, 6, &next_weight),
check_combined_proof_size::<<Test as Config>::RuntimeCall>(
&info,
&maximum_weight,
6,
&next_weight
),
InvalidTransaction::ExhaustsResources
);
assert_ok!(check_combined_proof_size::<<Test as Config>::RuntimeCall>(
&mandatory,
&maximum_weight,
6,
&next_weight
));
}
}
Loading