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
//! Block resource limits configuration structures.
19
//!
20
//! FRAME defines two resources that are limited within a block:
21
//! - Weight (execution cost/time)
22
//! - Length (block size)
23
//!
24
//! `frame_system` tracks consumption of each of these resources separately for each
25
//! `DispatchClass`. This module contains configuration object for both resources,
26
//! which should be passed to `frame_system` configuration when runtime is being set up.
27

            
28
use frame_support::{
29
	dispatch::{DispatchClass, OneOrMany, PerDispatchClass},
30
	weights::{constants, Weight},
31
};
32
use scale_info::TypeInfo;
33
use sp_runtime::{traits::Bounded, Perbill, RuntimeDebug};
34

            
35
/// Block length limit configuration.
36
#[derive(RuntimeDebug, Clone, codec::Encode, codec::Decode, TypeInfo)]
37
pub struct BlockLength {
38
	/// Maximal total length in bytes for each extrinsic class.
39
	///
40
	/// In the worst case, the total block length is going to be:
41
	/// `MAX(max)`
42
	pub max: PerDispatchClass<u32>,
43
}
44

            
45
impl Default for BlockLength {
46
	fn default() -> Self {
47
		BlockLength::max_with_normal_ratio(5 * 1024 * 1024, DEFAULT_NORMAL_RATIO)
48
	}
49
}
50

            
51
impl BlockLength {
52
	/// Create new `BlockLength` with `max` for every class.
53
	pub fn max(max: u32) -> Self {
54
		Self { max: PerDispatchClass::new(|_| max) }
55
	}
56

            
57
	/// Create new `BlockLength` with `max` for `Operational` & `Mandatory`
58
	/// and `normal * max` for `Normal`.
59
259684
	pub fn max_with_normal_ratio(max: u32, normal: Perbill) -> Self {
60
259684
		Self {
61
779052
			max: PerDispatchClass::new(|class| {
62
779052
				if class == DispatchClass::Normal {
63
259684
					normal * max
64
				} else {
65
519368
					max
66
				}
67
779052
			}),
68
259684
		}
69
259684
	}
70
}
71

            
72
#[derive(Default, RuntimeDebug)]
73
pub struct ValidationErrors {
74
	pub has_errors: bool,
75
	#[cfg(feature = "std")]
76
	pub errors: Vec<String>,
77
}
78

            
79
macro_rules! error_assert {
80
	($cond : expr, $err : expr, $format : expr $(, $params: expr )*$(,)*) => {
81
		if !$cond {
82
			$err.has_errors = true;
83
			#[cfg(feature = "std")]
84
			{ $err.errors.push(format!($format $(, &$params )*)); }
85
		}
86
	}
87
}
88

            
89
/// A result of validating `BlockWeights` correctness.
90
pub type ValidationResult = Result<BlockWeights, ValidationErrors>;
91

            
92
/// A ratio of `Normal` dispatch class within block, used as default value for
93
/// `BlockWeight` and `BlockLength`. The `Default` impls are provided mostly for convenience
94
/// to use in tests.
95
const DEFAULT_NORMAL_RATIO: Perbill = Perbill::from_percent(75);
96

            
97
/// `DispatchClass`-specific weight configuration.
98
#[derive(RuntimeDebug, Clone, codec::Encode, codec::Decode, TypeInfo)]
99
pub struct WeightsPerClass {
100
	/// Base weight of single extrinsic of given class.
101
	pub base_extrinsic: Weight,
102
	/// Maximal weight of single extrinsic. Should NOT include `base_extrinsic` cost.
103
	///
104
	/// `None` indicates that this class of extrinsics doesn't have a limit.
105
	pub max_extrinsic: Option<Weight>,
106
	/// Block maximal total weight for all extrinsics of given class.
107
	///
108
	/// `None` indicates that weight sum of this class of extrinsics is not
109
	/// restricted. Use this value carefully, since it might produce heavily oversized
110
	/// blocks.
111
	///
112
	/// In the worst case, the total weight consumed by the class is going to be:
113
	/// `MAX(max_total) + MAX(reserved)`.
114
	pub max_total: Option<Weight>,
115
	/// Block reserved allowance for all extrinsics of a particular class.
116
	///
117
	/// Setting to `None` indicates that extrinsics of that class are allowed
118
	/// to go over total block weight (but at most `max_total` for that class).
119
	/// Setting to `Some(x)` guarantees that at least `x` weight of particular class
120
	/// is processed in every block.
121
	pub reserved: Option<Weight>,
122
}
123

            
124
/// Block weight limits & base values configuration.
125
///
126
/// This object is responsible for defining weight limits and base weight values tracked
127
/// during extrinsic execution.
128
///
129
/// Each block starts with `base_block` weight being consumed right away. Next up the
130
/// `on_initialize` pallet callbacks are invoked and their cost is added before any extrinsic
131
/// is executed. This cost is tracked as `Mandatory` dispatch class.
132
///
133
/// ```text,ignore
134
/// |   | `max_block`    |   |
135
/// |   |                |   |
136
/// |   |                |   |
137
/// |   |                |   |
138
/// |   |                |  #| `on_initialize`
139
/// |  #| `base_block`   |  #|
140
/// |NOM|                |NOM|
141
///  ||\_ Mandatory
142
///  |\__ Operational
143
///  \___ Normal
144
/// ```
145
///
146
/// The remaining capacity can be used to dispatch extrinsics. Note that each dispatch class
147
/// is being tracked separately, but the sum can't exceed `max_block` (except for `reserved`).
148
/// Below you can see a picture representing full block with 3 extrinsics (two `Operational` and
149
/// one `Normal`). Each class has it's own limit `max_total`, but also the sum cannot exceed
150
/// `max_block` value.
151
///
152
/// ```text,ignore
153
///                          -- `Mandatory` limit (unlimited)
154
/// | # |                 |   |
155
/// | # | `Ext3`          | - - `Operational` limit
156
/// |#  | `Ext2`          |-  - `Normal` limit
157
/// | # | `Ext1`          | # |
158
/// |  #| `on_initialize` | ##|
159
/// |  #| `base_block`    |###|
160
/// |NOM|                 |NOM|
161
/// ```
162
///
163
/// It should be obvious now that it's possible for one class to reach it's limit (say `Normal`),
164
/// while the block has still capacity to process more transactions (`max_block` not reached,
165
/// `Operational` transactions can still go in). Setting `max_total` to `None` disables the
166
/// per-class limit. This is generally highly recommended for `Mandatory` dispatch class, while it
167
/// can be dangerous for `Normal` class and should only be done with extra care and consideration.
168
///
169
/// Often it's desirable for some class of transactions to be added to the block despite it being
170
/// full. For instance one might want to prevent high-priority `Normal` transactions from pushing
171
/// out lower-priority `Operational` transactions. In such cases you might add a `reserved` capacity
172
/// for given class.
173
///
174
/// ```test,ignore
175
///              _
176
///   #           \
177
///   #   `Ext8`   - `reserved`
178
///   #          _/
179
/// | # | `Ext7                 | - - `Operational` limit
180
/// |#  | `Ext6`                |   |
181
/// |#  | `Ext5`                |-# - `Normal` limit
182
/// |#  | `Ext4`                |## |
183
/// |  #| `on_initialize`       |###|
184
/// |  #| `base_block`          |###|
185
/// |NOM|                       |NOM|
186
/// ```
187
///
188
/// In the above example, `Ext4-6` fill up the block almost up to `max_block`. `Ext7` would not fit
189
/// if there wasn't the extra `reserved` space for `Operational` transactions. Note that `max_total`
190
/// limit applies to `reserved` space as well (i.e. the sum of weights of `Ext7` & `Ext8` mustn't
191
/// exceed it). Setting `reserved` to `None` allows the extrinsics to always get into the block up
192
/// to their `max_total` limit. If `max_total` is set to `None` as well, all extrinsics witch
193
/// dispatchables of given class will always end up in the block (recommended for `Mandatory`
194
/// dispatch class).
195
///
196
/// As a consequence of `reserved` space, total consumed block weight might exceed `max_block`
197
/// value, so this parameter should rather be thought of as "target block weight" than a hard limit.
198
#[derive(RuntimeDebug, Clone, codec::Encode, codec::Decode, TypeInfo)]
199
pub struct BlockWeights {
200
	/// Base weight of block execution.
201
	pub base_block: Weight,
202
	/// Maximal total weight consumed by all kinds of extrinsics (without `reserved` space).
203
	pub max_block: Weight,
204
	/// Weight limits for extrinsics of given dispatch class.
205
	pub per_class: PerDispatchClass<WeightsPerClass>,
206
}
207

            
208
impl Default for BlockWeights {
209
	fn default() -> Self {
210
		Self::with_sensible_defaults(
211
			Weight::from_parts(constants::WEIGHT_REF_TIME_PER_SECOND, u64::MAX),
212
			DEFAULT_NORMAL_RATIO,
213
		)
214
	}
215
}
216

            
217
impl BlockWeights {
218
	/// Get per-class weight settings.
219
2162178
	pub fn get(&self, class: DispatchClass) -> &WeightsPerClass {
220
2162178
		self.per_class.get(class)
221
2162178
	}
222

            
223
	/// Verifies correctness of this `BlockWeights` object.
224
1679810
	pub fn validate(self) -> ValidationResult {
225
10078860
		fn or_max(w: Option<Weight>) -> Weight {
226
10078860
			w.unwrap_or_else(Weight::max_value)
227
10078860
		}
228
1679810
		let mut error = ValidationErrors::default();
229

            
230
6719240
		for class in DispatchClass::all() {
231
5039430
			let weights = self.per_class.get(*class);
232
5039430
			let max_for_class = or_max(weights.max_total);
233
5039430
			let base_for_class = weights.base_extrinsic;
234
5039430
			let reserved = or_max(weights.reserved);
235
5039430
			// Make sure that if total is set it's greater than base_block &&
236
5039430
			// base_for_class
237
5039430
			error_assert!(
238
5039430
				(max_for_class.all_gt(self.base_block) && max_for_class.all_gt(base_for_class))
239
				|| max_for_class == Weight::zero(),
240
				&mut error,
241
				"[{:?}] {:?} (total) has to be greater than {:?} (base block) & {:?} (base extrinsic)",
242
				class, max_for_class, self.base_block, base_for_class,
243
			);
244
			// Max extrinsic can't be greater than max_for_class.
245
5039430
			error_assert!(
246
5039430
				weights
247
5039430
					.max_extrinsic
248
5039430
					.unwrap_or(Weight::zero())
249
5039430
					.all_lte(max_for_class.saturating_sub(base_for_class)),
250
				&mut error,
251
				"[{:?}] {:?} (max_extrinsic) can't be greater than {:?} (max for class)",
252
				class,
253
				weights.max_extrinsic,
254
				max_for_class.saturating_sub(base_for_class),
255
			);
256
			// Max extrinsic should not be 0
257
5039430
			error_assert!(
258
5039430
				weights.max_extrinsic.unwrap_or_else(Weight::max_value).all_gt(Weight::zero()),
259
				&mut error,
260
				"[{:?}] {:?} (max_extrinsic) must not be 0. Check base cost and average initialization cost.",
261
				class, weights.max_extrinsic,
262
			);
263
			// Make sure that if reserved is set it's greater than base_for_class.
264
5039430
			error_assert!(
265
5039430
				reserved.all_gt(base_for_class) || reserved == Weight::zero(),
266
				&mut error,
267
				"[{:?}] {:?} (reserved) has to be greater than {:?} (base extrinsic) if set",
268
				class,
269
				reserved,
270
				base_for_class,
271
			);
272
			// Make sure max block is greater than max_total if it's set.
273
5039430
			error_assert!(
274
5039430
				self.max_block.all_gte(weights.max_total.unwrap_or(Weight::zero())),
275
				&mut error,
276
				"[{:?}] {:?} (max block) has to be greater than {:?} (max for class)",
277
				class,
278
				self.max_block,
279
				weights.max_total,
280
			);
281
			// Make sure we can fit at least one extrinsic.
282
5039430
			error_assert!(
283
5039430
				self.max_block.all_gt(base_for_class + self.base_block),
284
				&mut error,
285
				"[{:?}] {:?} (max block) must fit at least one extrinsic {:?} (base weight)",
286
				class,
287
				self.max_block,
288
				base_for_class + self.base_block,
289
			);
290
		}
291

            
292
1679810
		if error.has_errors {
293
			Err(error)
294
		} else {
295
1679810
			Ok(self)
296
		}
297
1679810
	}
298

            
299
	/// Create new weights definition, with both `Normal` and `Operational`
300
	/// classes limited to given weight.
301
	///
302
	/// Note there is no reservation for `Operational` class, so this constructor
303
	/// is not suitable for production deployments.
304
	pub fn simple_max(block_weight: Weight) -> Self {
305
		Self::builder()
306
			.base_block(Weight::zero())
307
			.for_class(DispatchClass::all(), |weights| {
308
				weights.base_extrinsic = Weight::zero();
309
			})
310
			.for_class(DispatchClass::non_mandatory(), |weights| {
311
				weights.max_total = block_weight.into();
312
			})
313
			.build()
314
			.expect("We only specify max_total and leave base values as defaults; qed")
315
	}
316

            
317
	/// Create a sensible default weights system given only expected maximal block weight and the
318
	/// ratio that `Normal` extrinsics should occupy.
319
	///
320
	/// Assumptions:
321
	///  - Average block initialization is assumed to be `10%`.
322
	///  - `Operational` transactions have reserved allowance (`1.0 - normal_ratio`)
323
	pub fn with_sensible_defaults(expected_block_weight: Weight, normal_ratio: Perbill) -> Self {
324
		let normal_weight = normal_ratio * expected_block_weight;
325
		Self::builder()
326
			.for_class(DispatchClass::Normal, |weights| {
327
				weights.max_total = normal_weight.into();
328
			})
329
			.for_class(DispatchClass::Operational, |weights| {
330
				weights.max_total = expected_block_weight.into();
331
				weights.reserved = (expected_block_weight - normal_weight).into();
332
			})
333
			.avg_block_initialization(Perbill::from_percent(10))
334
			.build()
335
			.expect("Sensible defaults are tested to be valid; qed")
336
	}
337

            
338
	/// Start constructing new `BlockWeights` object.
339
	///
340
	/// By default all kinds except of `Mandatory` extrinsics are disallowed.
341
1644052
	pub fn builder() -> BlockWeightsBuilder {
342
1644052
		BlockWeightsBuilder {
343
1644052
			weights: BlockWeights {
344
1644052
				base_block: constants::BlockExecutionWeight::get(),
345
1644052
				max_block: Weight::zero(),
346
4932156
				per_class: PerDispatchClass::new(|class| {
347
4932156
					let initial =
348
4932156
						if class == DispatchClass::Mandatory { None } else { Some(Weight::zero()) };
349
4932156
					WeightsPerClass {
350
4932156
						base_extrinsic: constants::ExtrinsicBaseWeight::get(),
351
4932156
						max_extrinsic: None,
352
4932156
						max_total: initial,
353
4932156
						reserved: initial,
354
4932156
					}
355
4932156
				}),
356
1644052
			},
357
1644052
			init_cost: None,
358
1644052
		}
359
1644052
	}
360
}
361

            
362
/// An opinionated builder for `Weights` object.
363
pub struct BlockWeightsBuilder {
364
	weights: BlockWeights,
365
	init_cost: Option<Perbill>,
366
}
367

            
368
impl BlockWeightsBuilder {
369
	/// Set base block weight.
370
4110130
	pub fn base_block(mut self, base_block: Weight) -> Self {
371
4110130
		self.weights.base_block = base_block;
372
4110130
		self
373
4110130
	}
374

            
375
	/// Average block initialization weight cost.
376
	///
377
	/// This value is used to derive maximal allowed extrinsic weight for each
378
	/// class, based on the allowance.
379
	///
380
	/// This is to make sure that extrinsics don't stay forever in the pool,
381
	/// because they could seemingly fit the block (since they are below `max_block`),
382
	/// but the cost of calling `on_initialize` always prevents them from being included.
383
4110130
	pub fn avg_block_initialization(mut self, init_cost: Perbill) -> Self {
384
4110130
		self.init_cost = Some(init_cost);
385
4110130
		self
386
4110130
	}
387

            
388
	/// Set parameters for particular class.
389
	///
390
	/// Note: `None` values of `max_extrinsic` will be overwritten in `build` in case
391
	/// `avg_block_initialization` rate is set to a non-zero value.
392
7398234
	pub fn for_class(
393
7398234
		mut self,
394
7398234
		class: impl OneOrMany<DispatchClass>,
395
7398234
		action: impl Fn(&mut WeightsPerClass),
396
7398234
	) -> Self {
397
12330390
		for class in class.into_iter() {
398
12330390
			action(self.weights.per_class.get_mut(class));
399
12330390
		}
400
7398234
		self
401
7398234
	}
402

            
403
	/// Construct the `BlockWeights` object.
404
1644052
	pub fn build(self) -> ValidationResult {
405
1644052
		// compute max extrinsic size
406
1644052
		let Self { mut weights, init_cost } = self;
407

            
408
		// compute max block size.
409
6576208
		for class in DispatchClass::all() {
410
4932156
			weights.max_block = match weights.per_class.get(*class).max_total {
411
3288104
				Some(max) => max.max(weights.max_block),
412
1644052
				_ => weights.max_block,
413
			};
414
		}
415
		// compute max size of single extrinsic
416
1644052
		if let Some(init_weight) = init_cost.map(|rate| rate * weights.max_block) {
417
6576208
			for class in DispatchClass::all() {
418
4932156
				let per_class = weights.per_class.get_mut(*class);
419
4932156
				if per_class.max_extrinsic.is_none() && init_cost.is_some() {
420
4932156
					per_class.max_extrinsic = per_class
421
4932156
						.max_total
422
4932156
						.map(|x| x.saturating_sub(init_weight))
423
4932156
						.map(|x| x.saturating_sub(per_class.base_extrinsic));
424
4932156
				}
425
			}
426
		}
427

            
428
		// Validate the result
429
1644052
		weights.validate()
430
1644052
	}
431

            
432
	/// Construct the `BlockWeights` object or panic if it's invalid.
433
	///
434
	/// This is a convenience method to be called whenever you construct a runtime.
435
1644052
	pub fn build_or_panic(self) -> BlockWeights {
436
1644052
		self.build().expect(
437
1644052
			"Builder finished with `build_or_panic`; The panic is expected if runtime weights are not correct"
438
1644052
		)
439
1644052
	}
440
}
441

            
442
#[cfg(test)]
443
mod tests {
444
	use super::*;
445

            
446
	#[test]
447
	fn default_weights_are_valid() {
448
		BlockWeights::default().validate().unwrap();
449
	}
450
}