referrerpolicy=no-referrer-when-downgrade

sp_weights/
lib.rs

1// This file is part of Substrate.
2
3// Copyright (C) Parity Technologies (UK) Ltd.
4// SPDX-License-Identifier: Apache-2.0
5
6// Licensed under the Apache License, Version 2.0 (the "License");
7// you may not use this file except in compliance with the License.
8// You may obtain a copy of the License at
9//
10// 	http://www.apache.org/licenses/LICENSE-2.0
11//
12// Unless required by applicable law or agreed to in writing, software
13// distributed under the License is distributed on an "AS IS" BASIS,
14// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15// See the License for the specific language governing permissions and
16// limitations under the License.
17
18//! # Primitives for transaction weighting.
19
20#![cfg_attr(not(feature = "std"), no_std)]
21
22extern crate self as sp_weights;
23
24mod weight_meter;
25mod weight_v2;
26
27use bounded_collections::Get;
28use codec::{Decode, Encode};
29use scale_info::TypeInfo;
30#[cfg(feature = "serde")]
31use serde::{Deserialize, Serialize};
32use smallvec::SmallVec;
33use sp_arithmetic::{
34	traits::{BaseArithmetic, SaturatedConversion, Unsigned},
35	Perbill,
36};
37
38pub use weight_meter::*;
39pub use weight_v2::*;
40
41pub mod constants {
42	pub const WEIGHT_REF_TIME_PER_SECOND: u64 = 1_000_000_000_000;
43	pub const WEIGHT_REF_TIME_PER_MILLIS: u64 = 1_000_000_000;
44	pub const WEIGHT_REF_TIME_PER_MICROS: u64 = 1_000_000;
45	pub const WEIGHT_REF_TIME_PER_NANOS: u64 = 1_000;
46
47	pub const WEIGHT_PROOF_SIZE_PER_MB: u64 = 1024 * 1024;
48	pub const WEIGHT_PROOF_SIZE_PER_KB: u64 = 1024;
49}
50
51/// The weight of database operations that the runtime can invoke.
52///
53/// NOTE: This is currently only measured in computational time, and will probably
54/// be updated all together once proof size is accounted for.
55#[derive(Clone, Copy, Eq, PartialEq, Default, Debug, Encode, Decode, TypeInfo)]
56pub struct RuntimeDbWeight {
57	pub read: u64,
58	pub write: u64,
59}
60
61impl RuntimeDbWeight {
62	pub fn reads(self, r: u64) -> Weight {
63		Weight::from_parts(self.read.saturating_mul(r), 0)
64	}
65
66	pub fn writes(self, w: u64) -> Weight {
67		Weight::from_parts(self.write.saturating_mul(w), 0)
68	}
69
70	pub fn reads_writes(self, r: u64, w: u64) -> Weight {
71		let read_weight = self.read.saturating_mul(r);
72		let write_weight = self.write.saturating_mul(w);
73		Weight::from_parts(read_weight.saturating_add(write_weight), 0)
74	}
75}
76
77/// One coefficient and its position in the `WeightToFee`.
78///
79/// One term of polynomial is calculated as:
80///
81/// ```ignore
82/// coeff_integer * x^(degree) + coeff_frac * x^(degree)
83/// ```
84///
85/// The `negative` value encodes whether the term is added or subtracted from the
86/// overall polynomial result.
87#[derive(Clone, Encode, Decode, TypeInfo)]
88pub struct WeightToFeeCoefficient<Balance> {
89	/// The integral part of the coefficient.
90	pub coeff_integer: Balance,
91	/// The fractional part of the coefficient.
92	pub coeff_frac: Perbill,
93	/// True iff the coefficient should be interpreted as negative.
94	pub negative: bool,
95	/// Degree/exponent of the term.
96	pub degree: u8,
97}
98
99impl<Balance> WeightToFeeCoefficient<Balance>
100where
101	Balance: BaseArithmetic + From<u32> + Copy + Unsigned,
102{
103	/// Evaluate the term at `x` and saturatingly amalgamate into `result`.
104	///
105	/// The unsigned value for the term is calculated as:
106	/// ```ignore
107	/// (frac * x^(degree) + integer * x^(degree))
108	/// ```
109	/// Depending on the value of `negative`, it is added or subtracted from the `result`.
110	pub fn saturating_eval(&self, mut result: Balance, x: Balance) -> Balance {
111		let power = x.saturating_pow(self.degree.into());
112
113		let frac = self.coeff_frac * power; // Overflow safe.
114		let integer = self.coeff_integer.saturating_mul(power);
115		// Do not add them together here to avoid an underflow.
116
117		if self.negative {
118			result = result.saturating_sub(frac);
119			result = result.saturating_sub(integer);
120		} else {
121			result = result.saturating_add(frac);
122			result = result.saturating_add(integer);
123		}
124
125		result
126	}
127}
128
129/// A list of coefficients that represent a polynomial.
130pub type WeightToFeeCoefficients<T> = SmallVec<[WeightToFeeCoefficient<T>; 4]>;
131
132/// A list of coefficients that represent a polynomial.
133///
134/// Can be [eval](Self::eval)uated at a specific `u64` to get the fee. The evaluations happens by
135/// summing up all term [results](`WeightToFeeCoefficient::saturating_eval`). The order of the
136/// coefficients matters since it uses saturating arithmetic. This struct does therefore not model a
137/// polynomial in the mathematical sense (polynomial ring).
138///
139/// For visualization purposes, the formulas of the unsigned terms look like:
140///
141/// ```ignore
142/// (c[0].frac * x^(c[0].degree) + c[0].integer * x^(c[0].degree))
143/// (c[1].frac * x^(c[1].degree) + c[1].integer * x^(c[1].degree))
144/// ...
145/// ```
146/// Depending on the value of `c[i].negative`, each term is added or subtracted from the result.
147/// The result is initialized as zero.
148pub struct FeePolynomial<Balance> {
149	coefficients: SmallVec<[WeightToFeeCoefficient<Balance>; 4]>,
150}
151
152impl<Balance> From<WeightToFeeCoefficients<Balance>> for FeePolynomial<Balance> {
153	fn from(coefficients: WeightToFeeCoefficients<Balance>) -> Self {
154		Self { coefficients }
155	}
156}
157
158impl<Balance> FeePolynomial<Balance>
159where
160	Balance: BaseArithmetic + From<u32> + Copy + Unsigned,
161{
162	/// Evaluate the polynomial at a specific `x`.
163	pub fn eval(&self, x: u64) -> Balance {
164		self.coefficients.iter().fold(Balance::zero(), |acc, term| {
165			term.saturating_eval(acc, Balance::saturated_from(x))
166		})
167	}
168}
169
170/// A trait that describes the weight to fee calculation.
171pub trait WeightToFee {
172	/// The type that is returned as result from calculation.
173	type Balance: BaseArithmetic + From<u32> + Copy + Unsigned;
174
175	/// Calculates the fee from the passed `weight`.
176	fn weight_to_fee(weight: &Weight) -> Self::Balance;
177}
178
179/// A trait that describes the weight to fee calculation as polynomial.
180///
181/// An implementor should only implement the `polynomial` function.
182pub trait WeightToFeePolynomial {
183	/// The type that is returned as result from polynomial evaluation.
184	type Balance: BaseArithmetic + From<u32> + Copy + Unsigned;
185
186	/// Returns a polynomial that describes the weight to fee conversion.
187	///
188	/// This is the only function that should be manually implemented. Please note
189	/// that all calculation is done in the probably unsigned `Balance` type. This means
190	/// that the order of coefficients is important as putting the negative coefficients
191	/// first will most likely saturate the result to zero mid evaluation.
192	fn polynomial() -> WeightToFeeCoefficients<Self::Balance>;
193}
194
195impl<T> WeightToFee for T
196where
197	T: WeightToFeePolynomial,
198{
199	type Balance = <Self as WeightToFeePolynomial>::Balance;
200
201	/// Calculates the fee from the passed `weight` according to the `polynomial`.
202	///
203	/// This should not be overridden in most circumstances. Calculation is done in the
204	/// `Balance` type and never overflows. All evaluation is saturating.
205	fn weight_to_fee(weight: &Weight) -> Self::Balance {
206		let poly: FeePolynomial<Self::Balance> = Self::polynomial().into();
207		poly.eval(weight.ref_time())
208	}
209}
210
211/// Implementor of `WeightToFee` that maps one unit of weight to one unit of fee.
212pub struct IdentityFee<T>(core::marker::PhantomData<T>);
213
214impl<T> WeightToFee for IdentityFee<T>
215where
216	T: BaseArithmetic + From<u32> + Copy + Unsigned,
217{
218	type Balance = T;
219
220	fn weight_to_fee(weight: &Weight) -> Self::Balance {
221		Self::Balance::saturated_from(weight.ref_time())
222	}
223}
224
225/// Implementor of [`WeightToFee`] such that it maps any unit of weight to a fixed fee.
226pub struct FixedFee<const F: u32, T>(core::marker::PhantomData<T>);
227
228impl<const F: u32, T> WeightToFee for FixedFee<F, T>
229where
230	T: BaseArithmetic + From<u32> + Copy + Unsigned,
231{
232	type Balance = T;
233
234	fn weight_to_fee(_: &Weight) -> Self::Balance {
235		F.into()
236	}
237}
238
239/// An implementation of [`WeightToFee`] that collects no fee.
240pub type NoFee<T> = FixedFee<0, T>;
241
242/// Implementor of [`WeightToFee`] that uses a constant multiplier.
243///
244/// # Example
245///
246/// ```
247/// # use bounded_collections::ConstU128;
248/// # use sp_weights::ConstantMultiplier;
249/// // Results in a multiplier of 10 for each unit of weight (or length)
250/// type LengthToFee = ConstantMultiplier::<u128, ConstU128<10u128>>;
251/// ```
252pub struct ConstantMultiplier<T, M>(core::marker::PhantomData<(T, M)>);
253
254impl<T, M> WeightToFee for ConstantMultiplier<T, M>
255where
256	T: BaseArithmetic + From<u32> + Copy + Unsigned,
257	M: Get<T>,
258{
259	type Balance = T;
260
261	fn weight_to_fee(weight: &Weight) -> Self::Balance {
262		Self::Balance::saturated_from(weight.ref_time()).saturating_mul(M::get())
263	}
264}
265
266#[cfg(test)]
267#[allow(dead_code)]
268mod tests {
269	use super::*;
270	use smallvec::smallvec;
271
272	type Balance = u64;
273
274	// 0.5x^3 + 2.333x^2 + 7x - 10_000
275	struct Poly;
276	impl WeightToFeePolynomial for Poly {
277		type Balance = Balance;
278
279		fn polynomial() -> WeightToFeeCoefficients<Self::Balance> {
280			smallvec![
281				WeightToFeeCoefficient {
282					coeff_integer: 0,
283					coeff_frac: Perbill::from_float(0.5),
284					negative: false,
285					degree: 3
286				},
287				WeightToFeeCoefficient {
288					coeff_integer: 2,
289					coeff_frac: Perbill::from_rational(1u32, 3u32),
290					negative: false,
291					degree: 2
292				},
293				WeightToFeeCoefficient {
294					coeff_integer: 7,
295					coeff_frac: Perbill::zero(),
296					negative: false,
297					degree: 1
298				},
299				WeightToFeeCoefficient {
300					coeff_integer: 10_000,
301					coeff_frac: Perbill::zero(),
302					negative: true,
303					degree: 0
304				},
305			]
306		}
307	}
308
309	#[test]
310	fn polynomial_works() {
311		// 100^3/2=500000 100^2*(2+1/3)=23333 700 -10000
312		assert_eq!(Poly::weight_to_fee(&Weight::from_parts(100, 0)), 514033);
313		// 10123^3/2=518677865433 10123^2*(2+1/3)=239108634 70861 -10000
314		assert_eq!(Poly::weight_to_fee(&Weight::from_parts(10_123, 0)), 518917034928);
315	}
316
317	#[test]
318	fn polynomial_does_not_underflow() {
319		assert_eq!(Poly::weight_to_fee(&Weight::zero()), 0);
320		assert_eq!(Poly::weight_to_fee(&Weight::from_parts(10, 0)), 0);
321	}
322
323	#[test]
324	fn polynomial_does_not_overflow() {
325		assert_eq!(Poly::weight_to_fee(&Weight::MAX), Balance::max_value() - 10_000);
326	}
327
328	#[test]
329	fn identity_fee_works() {
330		assert_eq!(IdentityFee::<Balance>::weight_to_fee(&Weight::zero()), 0);
331		assert_eq!(IdentityFee::<Balance>::weight_to_fee(&Weight::from_parts(50, 0)), 50);
332		assert_eq!(IdentityFee::<Balance>::weight_to_fee(&Weight::MAX), Balance::max_value());
333	}
334
335	#[test]
336	fn constant_fee_works() {
337		use bounded_collections::ConstU128;
338		assert_eq!(
339			ConstantMultiplier::<u128, ConstU128<100u128>>::weight_to_fee(&Weight::zero()),
340			0
341		);
342		assert_eq!(
343			ConstantMultiplier::<u128, ConstU128<10u128>>::weight_to_fee(&Weight::from_parts(
344				50, 0
345			)),
346			500
347		);
348		assert_eq!(
349			ConstantMultiplier::<u128, ConstU128<1024u128>>::weight_to_fee(&Weight::from_parts(
350				16, 0
351			)),
352			16384
353		);
354		assert_eq!(
355			ConstantMultiplier::<u128, ConstU128<{ u128::MAX }>>::weight_to_fee(
356				&Weight::from_parts(2, 0)
357			),
358			u128::MAX
359		);
360	}
361}