Skip to content

Commit 055eb53

Browse files
authored
StorageWeightReclaim: set to node pov size if higher (#5281)
This PR adds an additional defensive check to the reclaim SE. Since it can happen that we miss some storage accesses on other SEs pre-dispatch, we should double check that the bookkeeping of the runtime stays ahead of the node-side pov-size. If we discover a mismatch and the node-side pov-size is indeed higher, we should set the runtime bookkeeping to the node-side value. In cases such as #5229, we would stop including extrinsics and not run `on_idle` at least. cc @gui1117 --------- Co-authored-by: command-bot <>
1 parent b78d795 commit 055eb53

3 files changed

Lines changed: 112 additions & 3 deletions

File tree

cumulus/primitives/storage-weight-reclaim/src/lib.rs

Lines changed: 94 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -174,6 +174,9 @@ where
174174

175175
let storage_size_diff = benchmarked_weight.abs_diff(consumed_weight as u64);
176176

177+
let extrinsic_len = frame_system::AllExtrinsicsLen::<T>::get().unwrap_or(0);
178+
let node_side_pov_size = post_dispatch_proof_size.saturating_add(extrinsic_len.into());
179+
177180
// This value will be reclaimed by [`frame_system::CheckWeight`], so we need to calculate
178181
// that in.
179182
frame_system::BlockWeight::<T>::mutate(|current| {
@@ -190,6 +193,19 @@ where
190193
);
191194
current.reduce(Weight::from_parts(0, storage_size_diff), info.class)
192195
}
196+
197+
// If we encounter a situation where the node-side proof size is already higher than
198+
// what we have in the runtime bookkeeping, we add the difference to the `BlockWeight`.
199+
// This prevents that the proof size grows faster than the runtime proof size.
200+
let block_weight_proof_size = current.total().proof_size();
201+
let missing_from_node = node_side_pov_size.saturating_sub(block_weight_proof_size);
202+
if missing_from_node > 0 {
203+
log::warn!(
204+
target: LOG_TARGET,
205+
"Node-side PoV size higher than runtime proof size weight. node-side: {node_side_pov_size} extrinsic_len: {extrinsic_len} runtime: {block_weight_proof_size}, missing: {missing_from_node}. Setting to node-side proof size."
206+
);
207+
current.accrue(Weight::from_parts(0, missing_from_node), info.class);
208+
}
193209
});
194210
Ok(())
195211
}
@@ -332,6 +348,82 @@ mod tests {
332348
})
333349
}
334350

351+
#[test]
352+
fn sets_to_node_storage_proof_if_higher() {
353+
// The storage proof reported by the proof recorder is higher than what is stored on
354+
// the runtime side.
355+
{
356+
let mut test_ext = setup_test_externalities(&[1000, 1005]);
357+
358+
test_ext.execute_with(|| {
359+
// Stored in BlockWeight is 5
360+
set_current_storage_weight(5);
361+
362+
// Benchmarked storage weight: 10
363+
let info = DispatchInfo { weight: Weight::from_parts(0, 10), ..Default::default() };
364+
let post_info = PostDispatchInfo::default();
365+
366+
assert_ok!(CheckWeight::<Test>::do_pre_dispatch(&info, LEN));
367+
368+
let pre = StorageWeightReclaim::<Test>(PhantomData)
369+
.pre_dispatch(&ALICE, CALL, &info, LEN)
370+
.unwrap();
371+
assert_eq!(pre, Some(1000));
372+
373+
assert_ok!(CheckWeight::<Test>::post_dispatch(None, &info, &post_info, 0, &Ok(())));
374+
assert_ok!(StorageWeightReclaim::<Test>::post_dispatch(
375+
Some(pre),
376+
&info,
377+
&post_info,
378+
LEN,
379+
&Ok(())
380+
));
381+
382+
// We expect that the storage weight was set to the node-side proof size (1005) +
383+
// extrinsics length (150)
384+
assert_eq!(get_storage_weight().total().proof_size(), 1155);
385+
})
386+
}
387+
388+
// In this second scenario the proof size on the node side is only lower
389+
// after reclaim happened.
390+
{
391+
let mut test_ext = setup_test_externalities(&[175, 180]);
392+
test_ext.execute_with(|| {
393+
set_current_storage_weight(85);
394+
395+
// Benchmarked storage weight: 100
396+
let info =
397+
DispatchInfo { weight: Weight::from_parts(0, 100), ..Default::default() };
398+
let post_info = PostDispatchInfo::default();
399+
400+
// After this pre_dispatch, the BlockWeight proof size will be
401+
// 85 (initial) + 100 (benched) + 150 (tx length) = 335
402+
assert_ok!(CheckWeight::<Test>::do_pre_dispatch(&info, LEN));
403+
404+
let pre = StorageWeightReclaim::<Test>(PhantomData)
405+
.pre_dispatch(&ALICE, CALL, &info, LEN)
406+
.unwrap();
407+
assert_eq!(pre, Some(175));
408+
409+
assert_ok!(CheckWeight::<Test>::post_dispatch(None, &info, &post_info, 0, &Ok(())));
410+
411+
// First we will reclaim 95, which leaves us with 240 BlockWeight. This is lower
412+
// than 180 (proof size hf) + 150 (length), so we expect it to be set to 330.
413+
assert_ok!(StorageWeightReclaim::<Test>::post_dispatch(
414+
Some(pre),
415+
&info,
416+
&post_info,
417+
LEN,
418+
&Ok(())
419+
));
420+
421+
// We expect that the storage weight was set to the node-side proof weight
422+
assert_eq!(get_storage_weight().total().proof_size(), 330);
423+
})
424+
}
425+
}
426+
335427
#[test]
336428
fn does_nothing_without_extension() {
337429
let mut test_ext = new_test_ext();
@@ -545,7 +637,7 @@ mod tests {
545637

546638
#[test]
547639
fn test_nothing_relcaimed() {
548-
let mut test_ext = setup_test_externalities(&[100, 200]);
640+
let mut test_ext = setup_test_externalities(&[0, 100]);
549641

550642
test_ext.execute_with(|| {
551643
set_current_storage_weight(0);
@@ -568,7 +660,7 @@ mod tests {
568660
.pre_dispatch(&ALICE, CALL, &info, LEN)
569661
.unwrap();
570662
// Should return `setup_test_externalities` proof recorder value: 100.
571-
assert_eq!(pre, Some(100));
663+
assert_eq!(pre, Some(0));
572664

573665
// The `CheckWeight` extension will refund `actual_weight` from `PostDispatchInfo`
574666
// we always need to call `post_dispatch` to verify that they interoperate correctly.

prdoc/pr_5281.prdoc

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
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: PoV-Reclaim - Set `BlockWeight` to node-side PoV size if mismatch is detected
5+
6+
doc:
7+
- audience: Runtime Dev
8+
description: |
9+
After this change, the `StorageWeightReclaim` `SignedExtension` will check the node-side PoV size after every
10+
extrinsic. If we detect a case where the returned proof size is higher than the `BlockWeight` value of the
11+
runtime, we set `BlockWeight` to the size returned from the node.
12+
13+
crates:
14+
- name: cumulus-primitives-storage-weight-reclaim
15+
bump: patch
16+
- name: frame-system
17+
bump: minor

substrate/frame/system/src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -917,7 +917,7 @@ pub mod pallet {
917917

918918
/// Total length (in bytes) for all extrinsics put together, for the current block.
919919
#[pallet::storage]
920-
pub(super) type AllExtrinsicsLen<T: Config> = StorageValue<_, u32>;
920+
pub type AllExtrinsicsLen<T: Config> = StorageValue<_, u32>;
921921

922922
/// Map of block numbers to block hashes.
923923
#[pallet::storage]

0 commit comments

Comments
 (0)