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