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
//! # Utility Pallet
19
//! A stateless pallet with helpers for dispatch management which does no re-authentication.
20
//!
21
//! - [`Config`]
22
//! - [`Call`]
23
//!
24
//! ## Overview
25
//!
26
//! This pallet contains two basic pieces of functionality:
27
//! - Batch dispatch: A stateless operation, allowing any origin to execute multiple calls in a
28
//!   single dispatch. This can be useful to amalgamate proposals, combining `set_code` with
29
//!   corresponding `set_storage`s, for efficient multiple payouts with just a single signature
30
//!   verify, or in combination with one of the other two dispatch functionality.
31
//! - Pseudonymal dispatch: A stateless operation, allowing a signed origin to execute a call from
32
//!   an alternative signed origin. Each account has 2 * 2**16 possible "pseudonyms" (alternative
33
//!   account IDs) and these can be stacked. This can be useful as a key management tool, where you
34
//!   need multiple distinct accounts (e.g. as controllers for many staking accounts), but where
35
//!   it's perfectly fine to have each of them controlled by the same underlying keypair. Derivative
36
//!   accounts are, for the purposes of proxy filtering considered exactly the same as the origin
37
//!   and are thus hampered with the origin's filters.
38
//!
39
//! Since proxy filters are respected in all dispatches of this pallet, it should never need to be
40
//! filtered by any proxy.
41
//!
42
//! ## Interface
43
//!
44
//! ### Dispatchable Functions
45
//!
46
//! #### For batch dispatch
47
//! * `batch` - Dispatch multiple calls from the sender's origin.
48
//!
49
//! #### For pseudonymal dispatch
50
//! * `as_derivative` - Dispatch a call from a derivative signed origin.
51

            
52
// Ensure we're `no_std` when compiling for Wasm.
53
#![cfg_attr(not(feature = "std"), no_std)]
54

            
55
mod benchmarking;
56
mod tests;
57
pub mod weights;
58

            
59
extern crate alloc;
60

            
61
use alloc::{boxed::Box, vec::Vec};
62
use codec::{Decode, Encode};
63
use frame_support::{
64
	dispatch::{extract_actual_weight, GetDispatchInfo, PostDispatchInfo},
65
	traits::{IsSubType, OriginTrait, UnfilteredDispatchable},
66
};
67
use sp_core::TypeId;
68
use sp_io::hashing::blake2_256;
69
use sp_runtime::traits::{BadOrigin, Dispatchable, TrailingZeroInput};
70
pub use weights::WeightInfo;
71

            
72
pub use pallet::*;
73

            
74
28692
#[frame_support::pallet]
75
pub mod pallet {
76
	use super::*;
77
	use frame_support::pallet_prelude::*;
78
	use frame_system::pallet_prelude::*;
79

            
80
1698
	#[pallet::pallet]
81
	pub struct Pallet<T>(_);
82

            
83
	/// Configuration trait.
84
	#[pallet::config]
85
	pub trait Config: frame_system::Config {
86
		/// The overarching event type.
87
		type RuntimeEvent: From<Event> + IsType<<Self as frame_system::Config>::RuntimeEvent>;
88

            
89
		/// The overarching call type.
90
		type RuntimeCall: Parameter
91
			+ Dispatchable<RuntimeOrigin = Self::RuntimeOrigin, PostInfo = PostDispatchInfo>
92
			+ GetDispatchInfo
93
			+ From<frame_system::Call<Self>>
94
			+ UnfilteredDispatchable<RuntimeOrigin = Self::RuntimeOrigin>
95
			+ IsSubType<Call<Self>>
96
			+ IsType<<Self as frame_system::Config>::RuntimeCall>;
97

            
98
		/// The caller origin, overarching type of all pallets origins.
99
		type PalletsOrigin: Parameter +
100
			Into<<Self as frame_system::Config>::RuntimeOrigin> +
101
			IsType<<<Self as frame_system::Config>::RuntimeOrigin as frame_support::traits::OriginTrait>::PalletsOrigin>;
102

            
103
		/// Weight information for extrinsics in this pallet.
104
		type WeightInfo: WeightInfo;
105
	}
106

            
107
	#[pallet::event]
108
30792
	#[pallet::generate_deposit(pub(super) fn deposit_event)]
109
	pub enum Event {
110
		/// Batch of dispatches did not complete fully. Index of first failing dispatch given, as
111
		/// well as the error.
112
		BatchInterrupted { index: u32, error: DispatchError },
113
		/// Batch of dispatches completed fully with no error.
114
		BatchCompleted,
115
		/// Batch of dispatches completed but has errors.
116
		BatchCompletedWithErrors,
117
		/// A single item within a Batch of dispatches has completed with no error.
118
		ItemCompleted,
119
		/// A single item within a Batch of dispatches has completed with error.
120
		ItemFailed { error: DispatchError },
121
		/// A call was dispatched.
122
		DispatchedAs { result: DispatchResult },
123
	}
124

            
125
	// Align the call size to 1KB. As we are currently compiling the runtime for native/wasm
126
	// the `size_of` of the `Call` can be different. To ensure that this don't leads to
127
	// mismatches between native/wasm or to different metadata for the same runtime, we
128
	// algin the call size. The value is chosen big enough to hopefully never reach it.
129
	const CALL_ALIGN: u32 = 1024;
130

            
131
	#[pallet::extra_constants]
132
	impl<T: Config> Pallet<T> {
133
		/// The limit on the number of batched calls.
134
21654
		fn batched_calls_limit() -> u32 {
135
21654
			let allocator_limit = sp_core::MAX_POSSIBLE_ALLOCATION;
136
21654
			let call_size = ((core::mem::size_of::<<T as Config>::RuntimeCall>() as u32 +
137
21654
				CALL_ALIGN - 1) / CALL_ALIGN) *
138
21654
				CALL_ALIGN;
139
21654
			// The margin to take into account vec doubling capacity.
140
21654
			let margin_factor = 3;
141
21654

            
142
21654
			allocator_limit / margin_factor / call_size
143
21654
		}
144
	}
145

            
146
554367
	#[pallet::hooks]
147
	impl<T: Config> Hooks<BlockNumberFor<T>> for Pallet<T> {
148
53637
		fn integrity_test() {
149
53637
			// If you hit this error, you need to try to `Box` big dispatchable parameters.
150
53637
			assert!(
151
53637
				core::mem::size_of::<<T as Config>::RuntimeCall>() as u32 <= CALL_ALIGN,
152
				"Call enum size should be smaller than {} bytes.",
153
				CALL_ALIGN,
154
			);
155
53637
		}
156
	}
157

            
158
	#[pallet::error]
159
	pub enum Error<T> {
160
		/// Too many calls batched.
161
		TooManyCalls,
162
	}
163

            
164
177741
	#[pallet::call]
165
	impl<T: Config> Pallet<T> {
166
		/// Send a batch of dispatch calls.
167
		///
168
		/// May be called from any origin except `None`.
169
		///
170
		/// - `calls`: The calls to be dispatched from the same origin. The number of call must not
171
		///   exceed the constant: `batched_calls_limit` (available in constant metadata).
172
		///
173
		/// If origin is root then the calls are dispatched without checking origin filter. (This
174
		/// includes bypassing `frame_system::Config::BaseCallFilter`).
175
		///
176
		/// ## Complexity
177
		/// - O(C) where C is the number of calls to be batched.
178
		///
179
		/// This will return `Ok` in all circumstances. To determine the success of the batch, an
180
		/// event is deposited. If a call failed and the batch was interrupted, then the
181
		/// `BatchInterrupted` event is deposited, along with the number of successful calls made
182
		/// and the error of the failed call. If all were successful, then the `BatchCompleted`
183
		/// event is deposited.
184
		#[pallet::call_index(0)]
185
		#[pallet::weight({
186
22686
			let dispatch_infos = calls.iter().map(|call| call.get_dispatch_info()).collect::<Vec<_>>();
187
			let dispatch_weight = dispatch_infos.iter()
188
22686
				.map(|di| di.weight)
189
22686
				.fold(Weight::zero(), |total: Weight, weight: Weight| total.saturating_add(weight))
190
				.saturating_add(T::WeightInfo::batch(calls.len() as u32));
191
			let dispatch_class = {
192
				let all_operational = dispatch_infos.iter()
193
10158
					.map(|di| di.class)
194
10158
					.all(|class| class == DispatchClass::Operational);
195
				if all_operational {
196
					DispatchClass::Operational
197
				} else {
198
					DispatchClass::Normal
199
				}
200
			};
201
			(dispatch_weight, dispatch_class)
202
		})]
203
		pub fn batch(
204
			origin: OriginFor<T>,
205
			calls: Vec<<T as Config>::RuntimeCall>,
206
19890
		) -> DispatchResultWithPostInfo {
207
19890
			// Do not allow the `None` origin.
208
19890
			if ensure_none(origin.clone()).is_ok() {
209
				return Err(BadOrigin.into())
210
19890
			}
211
19890

            
212
19890
			let is_root = ensure_root(origin.clone()).is_ok();
213
19890
			let calls_len = calls.len();
214
19890
			ensure!(calls_len <= Self::batched_calls_limit() as usize, Error::<T>::TooManyCalls);
215

            
216
			// Track the actual weight of each of the batch calls.
217
19890
			let mut weight = Weight::zero();
218
19890
			for (index, call) in calls.into_iter().enumerate() {
219
6615
				let info = call.get_dispatch_info();
220
				// If origin is root, don't apply any dispatch filters; root can call anything.
221
6615
				let result = if is_root {
222
					call.dispatch_bypass_filter(origin.clone())
223
				} else {
224
6615
					call.dispatch(origin.clone())
225
				};
226
				// Add the weight of this call.
227
6615
				weight = weight.saturating_add(extract_actual_weight(&result, &info));
228
6615
				if let Err(e) = result {
229
1941
					Self::deposit_event(Event::BatchInterrupted {
230
1941
						index: index as u32,
231
1941
						error: e.error,
232
1941
					});
233
1941
					// Take the weight of this function itself into account.
234
1941
					let base_weight = T::WeightInfo::batch(index.saturating_add(1) as u32);
235
1941
					// Return the actual used weight + base_weight of this call.
236
1941
					return Ok(Some(base_weight + weight).into())
237
4674
				}
238
4674
				Self::deposit_event(Event::ItemCompleted);
239
			}
240
17949
			Self::deposit_event(Event::BatchCompleted);
241
17949
			let base_weight = T::WeightInfo::batch(calls_len as u32);
242
17949
			Ok(Some(base_weight + weight).into())
243
		}
244

            
245
		/// Send a call through an indexed pseudonym of the sender.
246
		///
247
		/// Filter from origin are passed along. The call will be dispatched with an origin which
248
		/// use the same filter as the origin of this call.
249
		///
250
		/// NOTE: If you need to ensure that any account-based filtering is not honored (i.e.
251
		/// because you expect `proxy` to have been used prior in the call stack and you do not want
252
		/// the call restrictions to apply to any sub-accounts), then use `as_multi_threshold_1`
253
		/// in the Multisig pallet instead.
254
		///
255
		/// NOTE: Prior to version *12, this was called `as_limited_sub`.
256
		///
257
		/// The dispatch origin for this call must be _Signed_.
258
		#[pallet::call_index(1)]
259
		#[pallet::weight({
260
			let dispatch_info = call.get_dispatch_info();
261
			(
262
				T::WeightInfo::as_derivative()
263
					// AccountData for inner call origin accountdata.
264
					.saturating_add(T::DbWeight::get().reads_writes(1, 1))
265
					.saturating_add(dispatch_info.weight),
266
				dispatch_info.class,
267
			)
268
		})]
269
		pub fn as_derivative(
270
			origin: OriginFor<T>,
271
			index: u16,
272
			call: Box<<T as Config>::RuntimeCall>,
273
6387
		) -> DispatchResultWithPostInfo {
274
6387
			let mut origin = origin;
275
6387
			let who = ensure_signed(origin.clone())?;
276
6387
			let pseudonym = Self::derivative_account_id(who, index);
277
6387
			origin.set_caller_from(frame_system::RawOrigin::Signed(pseudonym));
278
6387
			let info = call.get_dispatch_info();
279
6387
			let result = call.dispatch(origin);
280
6387
			// Always take into account the base weight of this call.
281
6387
			let mut weight = T::WeightInfo::as_derivative()
282
6387
				.saturating_add(T::DbWeight::get().reads_writes(1, 1));
283
6387
			// Add the real weight of the dispatch.
284
6387
			weight = weight.saturating_add(extract_actual_weight(&result, &info));
285
6387
			result
286
6387
				.map_err(|mut err| {
287
2595
					err.post_info = Some(weight).into();
288
2595
					err
289
6387
				})
290
6387
				.map(|_| Some(weight).into())
291
		}
292

            
293
		/// Send a batch of dispatch calls and atomically execute them.
294
		/// The whole transaction will rollback and fail if any of the calls failed.
295
		///
296
		/// May be called from any origin except `None`.
297
		///
298
		/// - `calls`: The calls to be dispatched from the same origin. The number of call must not
299
		///   exceed the constant: `batched_calls_limit` (available in constant metadata).
300
		///
301
		/// If origin is root then the calls are dispatched without checking origin filter. (This
302
		/// includes bypassing `frame_system::Config::BaseCallFilter`).
303
		///
304
		/// ## Complexity
305
		/// - O(C) where C is the number of calls to be batched.
306
		#[pallet::call_index(2)]
307
		#[pallet::weight({
308
1491
			let dispatch_infos = calls.iter().map(|call| call.get_dispatch_info()).collect::<Vec<_>>();
309
			let dispatch_weight = dispatch_infos.iter()
310
1491
				.map(|di| di.weight)
311
1491
				.fold(Weight::zero(), |total: Weight, weight: Weight| total.saturating_add(weight))
312
				.saturating_add(T::WeightInfo::batch_all(calls.len() as u32));
313
			let dispatch_class = {
314
				let all_operational = dispatch_infos.iter()
315
684
					.map(|di| di.class)
316
684
					.all(|class| class == DispatchClass::Operational);
317
				if all_operational {
318
					DispatchClass::Operational
319
				} else {
320
					DispatchClass::Normal
321
				}
322
			};
323
			(dispatch_weight, dispatch_class)
324
		})]
325
		pub fn batch_all(
326
			origin: OriginFor<T>,
327
			calls: Vec<<T as Config>::RuntimeCall>,
328
378
		) -> DispatchResultWithPostInfo {
329
378
			// Do not allow the `None` origin.
330
378
			if ensure_none(origin.clone()).is_ok() {
331
				return Err(BadOrigin.into())
332
378
			}
333
378

            
334
378
			let is_root = ensure_root(origin.clone()).is_ok();
335
378
			let calls_len = calls.len();
336
378
			ensure!(calls_len <= Self::batched_calls_limit() as usize, Error::<T>::TooManyCalls);
337

            
338
			// Track the actual weight of each of the batch calls.
339
378
			let mut weight = Weight::zero();
340
378
			for (index, call) in calls.into_iter().enumerate() {
341
237
				let info = call.get_dispatch_info();
342
				// If origin is root, bypass any dispatch filter; root can call anything.
343
237
				let result = if is_root {
344
					call.dispatch_bypass_filter(origin.clone())
345
				} else {
346
237
					let mut filtered_origin = origin.clone();
347
237
					// Don't allow users to nest `batch_all` calls.
348
237
					filtered_origin.add_filter(
349
544
						move |c: &<T as frame_system::Config>::RuntimeCall| {
350
465
							let c = <T as Config>::RuntimeCall::from_ref(c);
351
465
							!matches!(c.is_sub_type(), Some(Call::batch_all { .. }))
352
544
						},
353
237
					);
354
237
					call.dispatch(filtered_origin)
355
				};
356
				// Add the weight of this call.
357
237
				weight = weight.saturating_add(extract_actual_weight(&result, &info));
358
237
				result.map_err(|mut err| {
359
69
					// Take the weight of this function itself into account.
360
69
					let base_weight = T::WeightInfo::batch_all(index.saturating_add(1) as u32);
361
69
					// Return the actual used weight + base_weight of this call.
362
69
					err.post_info = Some(base_weight + weight).into();
363
69
					err
364
237
				})?;
365
168
				Self::deposit_event(Event::ItemCompleted);
366
			}
367
309
			Self::deposit_event(Event::BatchCompleted);
368
309
			let base_weight = T::WeightInfo::batch_all(calls_len as u32);
369
309
			Ok(Some(base_weight.saturating_add(weight)).into())
370
		}
371

            
372
		/// Dispatches a function call with a provided origin.
373
		///
374
		/// The dispatch origin for this call must be _Root_.
375
		///
376
		/// ## Complexity
377
		/// - O(1).
378
		#[pallet::call_index(3)]
379
		#[pallet::weight({
380
			let dispatch_info = call.get_dispatch_info();
381
			(
382
				T::WeightInfo::dispatch_as()
383
					.saturating_add(dispatch_info.weight),
384
				dispatch_info.class,
385
			)
386
		})]
387
		pub fn dispatch_as(
388
			origin: OriginFor<T>,
389
			as_origin: Box<T::PalletsOrigin>,
390
			call: Box<<T as Config>::RuntimeCall>,
391
51
		) -> DispatchResult {
392
51
			ensure_root(origin)?;
393

            
394
			let res = call.dispatch_bypass_filter((*as_origin).into());
395

            
396
			Self::deposit_event(Event::DispatchedAs {
397
				result: res.map(|_| ()).map_err(|e| e.error),
398
			});
399
			Ok(())
400
		}
401

            
402
		/// Send a batch of dispatch calls.
403
		/// Unlike `batch`, it allows errors and won't interrupt.
404
		///
405
		/// May be called from any origin except `None`.
406
		///
407
		/// - `calls`: The calls to be dispatched from the same origin. The number of call must not
408
		///   exceed the constant: `batched_calls_limit` (available in constant metadata).
409
		///
410
		/// If origin is root then the calls are dispatch without checking origin filter. (This
411
		/// includes bypassing `frame_system::Config::BaseCallFilter`).
412
		///
413
		/// ## Complexity
414
		/// - O(C) where C is the number of calls to be batched.
415
		#[pallet::call_index(4)]
416
		#[pallet::weight({
417
14868
			let dispatch_infos = calls.iter().map(|call| call.get_dispatch_info()).collect::<Vec<_>>();
418
			let dispatch_weight = dispatch_infos.iter()
419
14868
				.map(|di| di.weight)
420
14868
				.fold(Weight::zero(), |total: Weight, weight: Weight| total.saturating_add(weight))
421
				.saturating_add(T::WeightInfo::force_batch(calls.len() as u32));
422
			let dispatch_class = {
423
				let all_operational = dispatch_infos.iter()
424
7614
					.map(|di| di.class)
425
7614
					.all(|class| class == DispatchClass::Operational);
426
				if all_operational {
427
					DispatchClass::Operational
428
				} else {
429
					DispatchClass::Normal
430
				}
431
			};
432
			(dispatch_weight, dispatch_class)
433
		})]
434
		pub fn force_batch(
435
			origin: OriginFor<T>,
436
			calls: Vec<<T as Config>::RuntimeCall>,
437
1386
		) -> DispatchResultWithPostInfo {
438
1386
			// Do not allow the `None` origin.
439
1386
			if ensure_none(origin.clone()).is_ok() {
440
				return Err(BadOrigin.into())
441
1386
			}
442
1386

            
443
1386
			let is_root = ensure_root(origin.clone()).is_ok();
444
1386
			let calls_len = calls.len();
445
1386
			ensure!(calls_len <= Self::batched_calls_limit() as usize, Error::<T>::TooManyCalls);
446

            
447
			// Track the actual weight of each of the batch calls.
448
1386
			let mut weight = Weight::zero();
449
1386
			// Track failed dispatch occur.
450
1386
			let mut has_error: bool = false;
451
4365
			for call in calls.into_iter() {
452
4365
				let info = call.get_dispatch_info();
453
				// If origin is root, don't apply any dispatch filters; root can call anything.
454
4365
				let result = if is_root {
455
					call.dispatch_bypass_filter(origin.clone())
456
				} else {
457
4365
					call.dispatch(origin.clone())
458
				};
459
				// Add the weight of this call.
460
4365
				weight = weight.saturating_add(extract_actual_weight(&result, &info));
461
4365
				if let Err(e) = result {
462
1779
					has_error = true;
463
1779
					Self::deposit_event(Event::ItemFailed { error: e.error });
464
2586
				} else {
465
2586
					Self::deposit_event(Event::ItemCompleted);
466
2586
				}
467
			}
468
1386
			if has_error {
469
1215
				Self::deposit_event(Event::BatchCompletedWithErrors);
470
1215
			} else {
471
171
				Self::deposit_event(Event::BatchCompleted);
472
171
			}
473
1386
			let base_weight = T::WeightInfo::batch(calls_len as u32);
474
1386
			Ok(Some(base_weight.saturating_add(weight)).into())
475
		}
476

            
477
		/// Dispatch a function call with a specified weight.
478
		///
479
		/// This function does not check the weight of the call, and instead allows the
480
		/// Root origin to specify the weight of the call.
481
		///
482
		/// The dispatch origin for this call must be _Root_.
483
		#[pallet::call_index(5)]
484
		#[pallet::weight((*weight, call.get_dispatch_info().class))]
485
		pub fn with_weight(
486
			origin: OriginFor<T>,
487
			call: Box<<T as Config>::RuntimeCall>,
488
			weight: Weight,
489
573
		) -> DispatchResult {
490
573
			ensure_root(origin)?;
491
			let _ = weight; // Explicitly don't check the the weight witness.
492

            
493
			let res = call.dispatch_bypass_filter(frame_system::RawOrigin::Root.into());
494
			res.map(|_| ()).map_err(|e| e.error)
495
		}
496
	}
497
}
498

            
499
/// A pallet identifier. These are per pallet and should be stored in a registry somewhere.
500
#[derive(Clone, Copy, Eq, PartialEq, Encode, Decode)]
501
struct IndexedUtilityPalletId(u16);
502

            
503
impl TypeId for IndexedUtilityPalletId {
504
	const TYPE_ID: [u8; 4] = *b"suba";
505
}
506

            
507
impl<T: Config> Pallet<T> {
508
	/// Derive a derivative account ID from the owner account and the sub-account index.
509
6387
	pub fn derivative_account_id(who: T::AccountId, index: u16) -> T::AccountId {
510
6387
		let entropy = (b"modlpy/utilisuba", who, index).using_encoded(blake2_256);
511
6387
		Decode::decode(&mut TrailingZeroInput::new(entropy.as_ref()))
512
6387
			.expect("infinite length input; no invalid inputs for type; qed")
513
6387
	}
514
}