cumulus_primitives_storage_weight_reclaim/
lib.rs1#![cfg_attr(not(feature = "std"), no_std)]
19
20use codec::{Decode, DecodeWithMemTracking, Encode};
21use core::marker::PhantomData;
22use cumulus_primitives_core::Weight;
23use cumulus_primitives_proof_size_hostfunction::{
24 storage_proof_size::storage_proof_size, PROOF_RECORDING_DISABLED,
25};
26use frame_support::{
27 dispatch::{DispatchInfo, PostDispatchInfo},
28 weights::WeightMeter,
29};
30use frame_system::Config;
31use scale_info::TypeInfo;
32use sp_runtime::{
33 impl_tx_ext_default,
34 traits::{DispatchInfoOf, Dispatchable, PostDispatchInfoOf, TransactionExtension},
35 transaction_validity::TransactionValidityError,
36 DispatchResult,
37};
38
39#[cfg(test)]
40mod tests;
41
42const LOG_TARGET: &'static str = "runtime::storage_reclaim";
43
44#[doc = docify::embed!("src/tests.rs", simple_reclaimer_example)]
51pub struct StorageWeightReclaimer {
52 previous_remaining_proof_size: u64,
53 previous_reported_proof_size: Option<u64>,
54}
55
56impl StorageWeightReclaimer {
57 #[must_use = "Must call `reclaim_with_meter` to reclaim the weight"]
60 pub fn new(weight_meter: &WeightMeter) -> StorageWeightReclaimer {
61 let previous_remaining_proof_size = weight_meter.remaining().proof_size();
62 let previous_reported_proof_size = get_proof_size();
63 Self { previous_remaining_proof_size, previous_reported_proof_size }
64 }
65
66 fn reclaim(&mut self, remaining_weight: Weight) -> Option<Weight> {
68 let current_remaining_weight = remaining_weight.proof_size();
69 let current_storage_proof_size = get_proof_size()?;
70 let previous_storage_proof_size = self.previous_reported_proof_size?;
71 let used_weight =
72 self.previous_remaining_proof_size.saturating_sub(current_remaining_weight);
73 let reported_used_size =
74 current_storage_proof_size.saturating_sub(previous_storage_proof_size);
75 let reclaimable = used_weight.saturating_sub(reported_used_size);
76 log::trace!(
77 target: LOG_TARGET,
78 "Found reclaimable storage weight. benchmarked: {used_weight}, consumed: {reported_used_size}"
79 );
80
81 self.previous_remaining_proof_size = current_remaining_weight.saturating_add(reclaimable);
82 self.previous_reported_proof_size = Some(current_storage_proof_size);
83 Some(Weight::from_parts(0, reclaimable))
84 }
85
86 pub fn reclaim_with_meter(&mut self, weight_meter: &mut WeightMeter) -> Option<Weight> {
89 let reclaimed = self.reclaim(weight_meter.remaining())?;
90 weight_meter.reclaim_proof_size(reclaimed.proof_size());
91 Some(reclaimed)
92 }
93}
94
95pub fn get_proof_size() -> Option<u64> {
99 let proof_size = storage_proof_size();
100 (proof_size != PROOF_RECORDING_DISABLED).then_some(proof_size)
101}
102
103#[allow(deprecated)]
106mod allow_deprecated {
107 use super::*;
108
109 #[deprecated(note = "This extension doesn't provide accurate reclaim for storage intensive \
115 transaction extension pipeline; it ignores the validation and preparation of extensions prior \
116 to itself and ignores the post dispatch logic for extensions subsequent to itself, it also
117 doesn't provide weight information. \
118 Use `StorageWeightReclaim` in the `cumulus-pallet-weight-reclaim` crate")]
119 #[derive(Encode, Decode, DecodeWithMemTracking, Clone, Eq, PartialEq, Default, TypeInfo)]
120 #[scale_info(skip_type_params(T))]
121 pub struct StorageWeightReclaim<T: Config + Send + Sync>(pub(super) PhantomData<T>);
122}
123#[allow(deprecated)]
124pub use allow_deprecated::StorageWeightReclaim;
125
126#[allow(deprecated)]
127impl<T: Config + Send + Sync> StorageWeightReclaim<T> {
128 pub fn new() -> Self {
130 Self(Default::default())
131 }
132}
133
134#[allow(deprecated)]
135impl<T: Config + Send + Sync> core::fmt::Debug for StorageWeightReclaim<T> {
136 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> Result<(), core::fmt::Error> {
137 let _ = write!(f, "StorageWeightReclaim");
138 Ok(())
139 }
140}
141
142#[allow(deprecated)]
143impl<T: Config + Send + Sync> TransactionExtension<T::RuntimeCall> for StorageWeightReclaim<T>
144where
145 T::RuntimeCall: Dispatchable<Info = DispatchInfo, PostInfo = PostDispatchInfo>,
146{
147 const IDENTIFIER: &'static str = "StorageWeightReclaim";
148 type Implicit = ();
149 type Val = ();
150 type Pre = Option<u64>;
151
152 fn prepare(
153 self,
154 _val: Self::Val,
155 _origin: &T::RuntimeOrigin,
156 _call: &T::RuntimeCall,
157 _info: &DispatchInfoOf<T::RuntimeCall>,
158 _len: usize,
159 ) -> Result<Self::Pre, TransactionValidityError> {
160 Ok(get_proof_size())
161 }
162
163 fn post_dispatch_details(
164 pre: Self::Pre,
165 info: &DispatchInfoOf<T::RuntimeCall>,
166 post_info: &PostDispatchInfoOf<T::RuntimeCall>,
167 _len: usize,
168 _result: &DispatchResult,
169 ) -> Result<Weight, TransactionValidityError> {
170 let Some(pre_dispatch_proof_size) = pre else {
171 return Ok(Weight::zero());
172 };
173
174 let Some(post_dispatch_proof_size) = get_proof_size() else {
175 log::debug!(
176 target: LOG_TARGET,
177 "Proof recording enabled during pre-dispatch, now disabled. This should not happen."
178 );
179 return Ok(Weight::zero())
180 };
181 let unspent = post_info.calc_unspent(info).proof_size();
185 let benchmarked_weight = info.total_weight().proof_size().saturating_sub(unspent);
186 let consumed_weight = post_dispatch_proof_size.saturating_sub(pre_dispatch_proof_size);
187
188 let storage_size_diff = benchmarked_weight.abs_diff(consumed_weight as u64);
189
190 let extrinsic_len = frame_system::AllExtrinsicsLen::<T>::get().unwrap_or(0);
191 let node_side_pov_size = post_dispatch_proof_size.saturating_add(extrinsic_len.into());
192
193 frame_system::BlockWeight::<T>::mutate(|current| {
196 if consumed_weight > benchmarked_weight {
197 log::error!(
198 target: LOG_TARGET,
199 "Benchmarked storage weight smaller than consumed storage weight. extrinsic: {} benchmarked: {benchmarked_weight} consumed: {consumed_weight} unspent: {unspent}",
200 frame_system::Pallet::<T>::extrinsic_index().unwrap_or(0)
201 );
202 current.accrue(Weight::from_parts(0, storage_size_diff), info.class)
203 } else {
204 log::trace!(
205 target: LOG_TARGET,
206 "Reclaiming storage weight. extrinsic: {} benchmarked: {benchmarked_weight} consumed: {consumed_weight} unspent: {unspent}",
207 frame_system::Pallet::<T>::extrinsic_index().unwrap_or(0)
208 );
209 current.reduce(Weight::from_parts(0, storage_size_diff), info.class)
210 }
211
212 let block_weight_proof_size = current.total().proof_size();
216 let missing_from_node = node_side_pov_size.saturating_sub(block_weight_proof_size);
217 if missing_from_node > 0 {
218 log::debug!(
219 target: LOG_TARGET,
220 "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."
221 );
222 current.accrue(Weight::from_parts(0, missing_from_node), info.class);
223 }
224 });
225 Ok(Weight::zero())
226 }
227
228 impl_tx_ext_default!(T::RuntimeCall; weight validate);
229}