domain_check_weight/
lib.rs

1// Copyright (C) 2024 Subspace Labs, Inc.
2// SPDX-License-Identifier: Apache-2.0
3
4// Licensed under the Apache License, Version 2.0 (the "License");
5// you may not use this file except in compliance with the License.
6// You may obtain a copy of the License at
7//
8// 	http://www.apache.org/licenses/LICENSE-2.0
9//
10// Unless required by applicable law or agreed to in writing, software
11// distributed under the License is distributed on an "AS IS" BASIS,
12// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13// See the License for the specific language governing permissions and
14// limitations under the License.
15
16#![cfg_attr(not(feature = "std"), no_std)]
17
18use frame_support::dispatch::{DispatchInfo, PostDispatchInfo};
19use frame_support::traits::Get;
20use frame_system::limits::BlockWeights;
21use frame_system::{Config, ConsumedWeight};
22use parity_scale_codec::{Decode, Encode};
23use scale_info::TypeInfo;
24use sp_runtime::DispatchResult;
25use sp_runtime::traits::{
26    DispatchInfoOf, DispatchOriginOf, Dispatchable, PostDispatchInfoOf, TransactionExtension,
27    ValidateResult,
28};
29use sp_runtime::transaction_validity::{
30    TransactionSource, TransactionValidity, TransactionValidityError,
31};
32use sp_weights::Weight;
33
34/// Wrapper of [`frame_system::CheckWeight`]
35///
36/// It performs the same check as [`frame_system::CheckWeight`] except the `max_total/max_block` weight limit
37/// check is removed from the `pre_dispatch/pre_dispatch_unsigned` because the total weight of a domain block
38/// is based on probability instead of a hard limit.
39#[derive(Encode, Decode, Clone, Eq, PartialEq, Default, TypeInfo)]
40#[scale_info(skip_type_params(T))]
41pub struct CheckWeight<T: Config + Send + Sync>(core::marker::PhantomData<T>);
42
43impl<T: Config + Send + Sync> CheckWeight<T>
44where
45    T::RuntimeCall: Dispatchable<Info = DispatchInfo, PostInfo = PostDispatchInfo>,
46{
47    /// Creates new `SignedExtension` to check weight of the extrinsic.
48    pub fn new() -> Self {
49        Self(Default::default())
50    }
51
52    /// Check the block length and the max extrinsic weight and notes the new weight and length value.
53    ///
54    /// It is same as the [`frame_system::CheckWeight::do_prepare`] except the `limit_per_class` and
55    /// `max_total/max_block` weight limit checks are removed.
56    pub fn do_prepare(
57        info: &DispatchInfoOf<T::RuntimeCall>,
58        len: usize,
59        next_len: u32,
60    ) -> Result<(), TransactionValidityError> {
61        let all_weight = frame_system::Pallet::<T>::block_weight();
62        let maximum_weight = T::BlockWeights::get();
63        let next_weight =
64            calculate_consumed_weight::<T::RuntimeCall>(&maximum_weight, all_weight, info, len);
65
66        frame_system::AllExtrinsicsLen::<T>::put(next_len);
67        frame_system::BlockWeight::<T>::put(next_weight);
68
69        Ok(())
70    }
71}
72
73/// Calculate the new block weight value with the given extrinsic
74fn calculate_consumed_weight<Call>(
75    maximum_weight: &BlockWeights,
76    mut all_weight: ConsumedWeight,
77    info: &DispatchInfoOf<Call>,
78    len: usize,
79) -> ConsumedWeight
80where
81    Call: Dispatchable<Info = DispatchInfo, PostInfo = PostDispatchInfo>,
82{
83    // Also Consider extrinsic length as proof weight.
84    let extrinsic_weight = info
85        .total_weight()
86        .saturating_add(maximum_weight.get(info.class).base_extrinsic)
87        .saturating_add(Weight::from_parts(0, len as u64));
88
89    // Saturating add the weight
90    all_weight.accrue(extrinsic_weight, info.class);
91
92    all_weight
93}
94
95impl<T: Config + Send + Sync> TransactionExtension<T::RuntimeCall> for CheckWeight<T>
96where
97    T::RuntimeCall: Dispatchable<Info = DispatchInfo, PostInfo = PostDispatchInfo>,
98{
99    type Implicit = ();
100    type Pre = ();
101    type Val = u32;
102    const IDENTIFIER: &'static str = "CheckWeight";
103
104    fn weight(&self, _: &T::RuntimeCall) -> Weight {
105        // It is ok to use the upstream weight here, because this extension does fewer checks
106        // than the upstream `CheckWeight`. See the comment on `struct CheckWeight` for details.
107        <T::ExtensionsWeightInfo as frame_system::ExtensionsWeightInfo>::check_weight()
108    }
109
110    fn validate(
111        &self,
112        origin: T::RuntimeOrigin,
113        _call: &T::RuntimeCall,
114        info: &DispatchInfoOf<T::RuntimeCall>,
115        len: usize,
116        _self_implicit: Self::Implicit,
117        _inherited_implication: &impl Encode,
118        _source: TransactionSource,
119    ) -> ValidateResult<Self::Val, T::RuntimeCall> {
120        let (validity, next_len) = frame_system::CheckWeight::<T>::do_validate(info, len)?;
121        Ok((validity, next_len, origin))
122    }
123
124    fn prepare(
125        self,
126        val: Self::Val,
127        _origin: &DispatchOriginOf<T::RuntimeCall>,
128        _call: &T::RuntimeCall,
129        info: &DispatchInfoOf<T::RuntimeCall>,
130        len: usize,
131    ) -> Result<Self::Pre, TransactionValidityError> {
132        Self::do_prepare(info, len, val)
133    }
134
135    fn post_dispatch_details(
136        _pre: Self::Pre,
137        info: &DispatchInfoOf<T::RuntimeCall>,
138        post_info: &PostDispatchInfoOf<T::RuntimeCall>,
139        _len: usize,
140        _result: &DispatchResult,
141    ) -> Result<Weight, TransactionValidityError> {
142        frame_system::CheckWeight::<T>::do_post_dispatch(info, post_info)?;
143        Ok(Weight::zero())
144    }
145
146    fn bare_validate(
147        _call: &T::RuntimeCall,
148        info: &DispatchInfoOf<T::RuntimeCall>,
149        len: usize,
150    ) -> TransactionValidity {
151        Ok(frame_system::CheckWeight::<T>::do_validate(info, len)?.0)
152    }
153
154    fn bare_validate_and_prepare(
155        _call: &T::RuntimeCall,
156        info: &DispatchInfoOf<T::RuntimeCall>,
157        len: usize,
158    ) -> Result<(), TransactionValidityError> {
159        let (_, next_len) = frame_system::CheckWeight::<T>::do_validate(info, len)?;
160        Self::do_prepare(info, len, next_len)
161    }
162
163    fn bare_post_dispatch(
164        info: &DispatchInfoOf<T::RuntimeCall>,
165        post_info: &mut PostDispatchInfoOf<T::RuntimeCall>,
166        _len: usize,
167        _result: &DispatchResult,
168    ) -> Result<(), TransactionValidityError> {
169        frame_system::CheckWeight::<T>::do_post_dispatch(info, post_info)
170    }
171}
172
173impl<T: Config + Send + Sync> core::fmt::Debug for CheckWeight<T> {
174    #[cfg(feature = "std")]
175    fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
176        write!(f, "CheckWeight")
177    }
178
179    #[cfg(not(feature = "std"))]
180    fn fmt(&self, _: &mut core::fmt::Formatter) -> core::fmt::Result {
181        Ok(())
182    }
183}