@@ -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.
0 commit comments