cumulus_pallet_weight_reclaim/
lib.rs1#![cfg_attr(not(feature = "std"), no_std)]
26
27extern crate alloc;
28#[cfg(not(feature = "std"))]
29use alloc::vec::Vec;
30use codec::{Decode, DecodeWithMemTracking, Encode};
31use cumulus_primitives_storage_weight_reclaim::get_proof_size;
32use derive_where::derive_where;
33use frame_support::{
34 dispatch::{DispatchInfo, PostDispatchInfo},
35 pallet_prelude::Weight,
36 traits::Defensive,
37};
38use scale_info::TypeInfo;
39use sp_runtime::{
40 traits::{DispatchInfoOf, Dispatchable, Implication, PostDispatchInfoOf, TransactionExtension},
41 transaction_validity::{TransactionSource, TransactionValidityError, ValidTransaction},
42 DispatchResult,
43};
44
45#[cfg(feature = "runtime-benchmarks")]
46pub mod benchmarks;
47#[cfg(test)]
48mod tests;
49mod weights;
50
51pub use pallet::*;
52pub use weights::WeightInfo;
53
54const LOG_TARGET: &'static str = "runtime::storage_reclaim_pallet";
55
56#[frame_support::pallet]
59pub mod pallet {
60 use super::*;
61
62 #[pallet::pallet]
63 pub struct Pallet<T>(_);
64
65 #[pallet::config]
66 pub trait Config: frame_system::Config {
67 type WeightInfo: WeightInfo;
68 }
69}
70
71#[doc = docify::embed!("./src/tests.rs", Tx)]
75#[derive(Encode, Decode, DecodeWithMemTracking, TypeInfo)]
87#[derive_where(Clone, Eq, PartialEq, Default; S)]
88#[scale_info(skip_type_params(T))]
89pub struct StorageWeightReclaim<T, S>(pub S, core::marker::PhantomData<T>);
90
91impl<T, S> StorageWeightReclaim<T, S> {
92 pub fn new(s: S) -> Self {
94 Self(s, Default::default())
95 }
96}
97
98impl<T, S> From<S> for StorageWeightReclaim<T, S> {
99 fn from(s: S) -> Self {
100 Self::new(s)
101 }
102}
103
104impl<T, S: core::fmt::Debug> core::fmt::Debug for StorageWeightReclaim<T, S> {
105 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> Result<(), core::fmt::Error> {
106 #[cfg(feature = "std")]
107 let _ = write!(f, "StorageWeightReclaim<{:?}>", self.0);
108
109 #[cfg(not(feature = "std"))]
110 let _ = write!(f, "StorageWeightReclaim<wasm-stripped>");
111
112 Ok(())
113 }
114}
115
116impl<T: Config + Send + Sync, S: TransactionExtension<T::RuntimeCall>>
117 TransactionExtension<T::RuntimeCall> for StorageWeightReclaim<T, S>
118where
119 T::RuntimeCall: Dispatchable<Info = DispatchInfo, PostInfo = PostDispatchInfo>,
120{
121 const IDENTIFIER: &'static str = "StorageWeightReclaim<Use `metadata()`!>";
122
123 type Implicit = S::Implicit;
124
125 type Val = (Option<u64>, S::Val);
127
128 type Pre = (Option<u64>, S::Pre);
130
131 fn implicit(&self) -> Result<Self::Implicit, TransactionValidityError> {
132 self.0.implicit()
133 }
134
135 fn metadata() -> Vec<sp_runtime::traits::TransactionExtensionMetadata> {
136 let mut inner = S::metadata();
137 inner.push(sp_runtime::traits::TransactionExtensionMetadata {
138 identifier: "StorageWeightReclaim",
139 ty: scale_info::meta_type::<()>(),
140 implicit: scale_info::meta_type::<()>(),
141 });
142 inner
143 }
144
145 fn weight(&self, call: &T::RuntimeCall) -> Weight {
146 T::WeightInfo::storage_weight_reclaim().saturating_add(self.0.weight(call))
147 }
148
149 fn validate(
150 &self,
151 origin: T::RuntimeOrigin,
152 call: &T::RuntimeCall,
153 info: &DispatchInfoOf<T::RuntimeCall>,
154 len: usize,
155 self_implicit: Self::Implicit,
156 inherited_implication: &impl Implication,
157 source: TransactionSource,
158 ) -> Result<(ValidTransaction, Self::Val, T::RuntimeOrigin), TransactionValidityError> {
159 let proof_size = get_proof_size();
160
161 self.0
162 .validate(origin, call, info, len, self_implicit, inherited_implication, source)
163 .map(|(validity, val, origin)| (validity, (proof_size, val), origin))
164 }
165
166 fn prepare(
167 self,
168 val: Self::Val,
169 origin: &T::RuntimeOrigin,
170 call: &T::RuntimeCall,
171 info: &DispatchInfoOf<T::RuntimeCall>,
172 len: usize,
173 ) -> Result<Self::Pre, TransactionValidityError> {
174 let (proof_size, inner_val) = val;
175 self.0.prepare(inner_val, origin, call, info, len).map(|pre| (proof_size, pre))
176 }
177
178 fn post_dispatch_details(
179 pre: Self::Pre,
180 info: &DispatchInfoOf<T::RuntimeCall>,
181 post_info: &PostDispatchInfoOf<T::RuntimeCall>,
182 len: usize,
183 result: &DispatchResult,
184 ) -> Result<Weight, TransactionValidityError> {
185 let (proof_size_before_dispatch, inner_pre) = pre;
186
187 let mut post_info_with_inner = *post_info;
188 S::post_dispatch(inner_pre, info, &mut post_info_with_inner, len, result)?;
189
190 let inner_refund = if let (Some(before_weight), Some(after_weight)) =
191 (post_info.actual_weight, post_info_with_inner.actual_weight)
192 {
193 before_weight.saturating_sub(after_weight)
194 } else {
195 Weight::zero()
196 };
197
198 let Some(proof_size_before_dispatch) = proof_size_before_dispatch else {
199 return Ok(inner_refund);
201 };
202
203 let Some(proof_size_after_dispatch) = get_proof_size().defensive_proof(
204 "Proof recording enabled during prepare, now disabled. This should not happen.",
205 ) else {
206 return Ok(inner_refund)
207 };
208
209 let measured_proof_size =
211 proof_size_after_dispatch.saturating_sub(proof_size_before_dispatch);
212
213 let benchmarked_actual_weight = post_info_with_inner.calc_actual_weight(info);
218
219 let benchmarked_actual_proof_size = benchmarked_actual_weight.proof_size();
220 if benchmarked_actual_proof_size < measured_proof_size {
221 log::error!(
222 target: LOG_TARGET,
223 "Benchmarked storage weight smaller than consumed storage weight. \
224 benchmarked: {benchmarked_actual_proof_size} consumed: {measured_proof_size}"
225 );
226 } else {
227 log::trace!(
228 target: LOG_TARGET,
229 "Reclaiming storage weight. benchmarked: {benchmarked_actual_proof_size},
230 consumed: {measured_proof_size}"
231 );
232 }
233
234 let accurate_weight = benchmarked_actual_weight.set_proof_size(measured_proof_size);
235
236 let pov_size_missing_from_node = frame_system::BlockWeight::<T>::mutate(|current_weight| {
237 let already_reclaimed = frame_system::ExtrinsicWeightReclaimed::<T>::get();
238 current_weight.accrue(already_reclaimed, info.class);
239 current_weight.reduce(info.total_weight(), info.class);
240 current_weight.accrue(accurate_weight, info.class);
241
242 let extrinsic_len = frame_system::AllExtrinsicsLen::<T>::get().unwrap_or(0);
246 let node_side_pov_size = proof_size_after_dispatch.saturating_add(extrinsic_len.into());
247 let block_weight_proof_size = current_weight.total().proof_size();
248 let pov_size_missing_from_node =
249 node_side_pov_size.saturating_sub(block_weight_proof_size);
250 if pov_size_missing_from_node > 0 {
251 log::warn!(
252 target: LOG_TARGET,
253 "Node-side PoV size higher than runtime proof size weight. node-side: \
254 {node_side_pov_size} extrinsic_len: {extrinsic_len} runtime: \
255 {block_weight_proof_size}, missing: {pov_size_missing_from_node}. Setting to \
256 node-side proof size."
257 );
258 current_weight
259 .accrue(Weight::from_parts(0, pov_size_missing_from_node), info.class);
260 }
261
262 pov_size_missing_from_node
263 });
264
265 let accurate_unspent = info
269 .total_weight()
270 .saturating_sub(accurate_weight)
271 .saturating_sub(Weight::from_parts(0, pov_size_missing_from_node));
272 frame_system::ExtrinsicWeightReclaimed::<T>::put(accurate_unspent);
273
274 let already_unspent_in_tx_ext_pipeline = post_info.calc_unspent(info);
277 Ok(accurate_unspent.saturating_sub(already_unspent_in_tx_ext_pipeline))
278 }
279
280 fn bare_validate(
281 call: &T::RuntimeCall,
282 info: &DispatchInfoOf<T::RuntimeCall>,
283 len: usize,
284 ) -> frame_support::pallet_prelude::TransactionValidity {
285 S::bare_validate(call, info, len)
286 }
287
288 fn bare_validate_and_prepare(
289 call: &T::RuntimeCall,
290 info: &DispatchInfoOf<T::RuntimeCall>,
291 len: usize,
292 ) -> Result<(), TransactionValidityError> {
293 S::bare_validate_and_prepare(call, info, len)
294 }
295
296 fn bare_post_dispatch(
297 info: &DispatchInfoOf<T::RuntimeCall>,
298 post_info: &mut PostDispatchInfoOf<T::RuntimeCall>,
299 len: usize,
300 result: &DispatchResult,
301 ) -> Result<(), TransactionValidityError> {
302 S::bare_post_dispatch(info, post_info, len, result)?;
303
304 frame_system::Pallet::<T>::reclaim_weight(info, post_info)
305 }
306}