Skip to content

Commit 21a1bf8

Browse files
skunertgui1117
andauthored
[1.14] Backport PoV-reclaim fixes (#5273, #5281) (#5371)
Backports #5273 & #5281 --------- Co-authored-by: Guillaume Thiolliere <[email protected]>
1 parent c4b92f4 commit 21a1bf8

4 files changed

Lines changed: 165 additions & 8 deletions

File tree

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

Lines changed: 137 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -165,15 +165,17 @@ where
165165
);
166166
return Ok(())
167167
};
168-
let benchmarked_weight = info.weight.proof_size();
169-
let consumed_weight = post_dispatch_proof_size.saturating_sub(pre_dispatch_proof_size);
170-
171168
// Unspent weight according to the `actual_weight` from `PostDispatchInfo`
172169
// This unspent weight will be refunded by the `CheckWeight` extension, so we need to
173170
// account for that.
174171
let unspent = post_info.calc_unspent(info).proof_size();
175-
let storage_size_diff =
176-
benchmarked_weight.saturating_sub(unspent).abs_diff(consumed_weight as u64);
172+
let benchmarked_weight = info.weight.proof_size().saturating_sub(unspent);
173+
let consumed_weight = post_dispatch_proof_size.saturating_sub(pre_dispatch_proof_size);
174+
175+
let storage_size_diff = benchmarked_weight.abs_diff(consumed_weight as u64);
176+
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());
177179

178180
// This value will be reclaimed by [`frame_system::CheckWeight`], so we need to calculate
179181
// that in.
@@ -191,6 +193,19 @@ where
191193
);
192194
current.reduce(Weight::from_parts(0, storage_size_diff), info.class)
193195
}
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+
}
194209
});
195210
Ok(())
196211
}
@@ -294,6 +309,121 @@ mod tests {
294309
})
295310
}
296311

312+
#[test]
313+
fn underestimating_refund() {
314+
// We fixed a bug where `pre dispatch info weight > consumed weight > post info weight`
315+
// resulted in error.
316+
317+
// The real cost will be 100 bytes of storage size
318+
let mut test_ext = setup_test_externalities(&[0, 100]);
319+
320+
test_ext.execute_with(|| {
321+
set_current_storage_weight(1000);
322+
323+
// Benchmarked storage weight: 500
324+
let info = DispatchInfo { weight: Weight::from_parts(0, 101), ..Default::default() };
325+
let post_info = PostDispatchInfo {
326+
actual_weight: Some(Weight::from_parts(0, 99)),
327+
pays_fee: Default::default(),
328+
};
329+
330+
assert_ok!(CheckWeight::<Test>::do_pre_dispatch(&info, LEN));
331+
332+
let pre = StorageWeightReclaim::<Test>(PhantomData)
333+
.pre_dispatch(&ALICE, CALL, &info, LEN)
334+
.unwrap();
335+
assert_eq!(pre, Some(0));
336+
337+
assert_ok!(CheckWeight::<Test>::post_dispatch(None, &info, &post_info, 0, &Ok(())));
338+
// We expect an accrue of 1
339+
assert_ok!(StorageWeightReclaim::<Test>::post_dispatch(
340+
Some(pre),
341+
&info,
342+
&post_info,
343+
LEN,
344+
&Ok(())
345+
));
346+
347+
assert_eq!(get_storage_weight().total().proof_size(), 1250);
348+
})
349+
}
350+
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+
297427
#[test]
298428
fn does_nothing_without_extension() {
299429
let mut test_ext = new_test_ext();
@@ -507,7 +637,7 @@ mod tests {
507637

508638
#[test]
509639
fn test_nothing_relcaimed() {
510-
let mut test_ext = setup_test_externalities(&[100, 200]);
640+
let mut test_ext = setup_test_externalities(&[0, 100]);
511641

512642
test_ext.execute_with(|| {
513643
set_current_storage_weight(0);
@@ -530,7 +660,7 @@ mod tests {
530660
.pre_dispatch(&ALICE, CALL, &info, LEN)
531661
.unwrap();
532662
// Should return `setup_test_externalities` proof recorder value: 100.
533-
assert_eq!(pre, Some(100));
663+
assert_eq!(pre, Some(0));
534664

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

prdoc/pr_5273.prdoc

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
title: Fix storage weight reclaim bug.
2+
3+
doc:
4+
- audience: Runtime Dev
5+
description: |
6+
A bug in storage weight reclaim signed extension is fixed. The bug was causing an underestimate of the proof size when the post dispatch info was underestimating the proof size and the pre dispatch info was overestimating the proof size at the same time.
7+
8+
crates:
9+
- name: cumulus-primitives-storage-weight-reclaim
10+
bump: patch

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
@@ -914,7 +914,7 @@ pub mod pallet {
914914

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

919919
/// Map of block numbers to block hashes.
920920
#[pallet::storage]

0 commit comments

Comments
 (0)