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
//! > Made with *Substrate*, for *Polkadot*.
19
//!
20
//! [![github]](https://github.com/paritytech/polkadot-sdk/tree/master/substrate/frame/sudo)
21
//! [![polkadot]](https://polkadot.network)
22
//!
23
//! [github]: https://img.shields.io/badge/github-8da0cb?style=for-the-badge&labelColor=555555&logo=github
24
//! [polkadot]: https://img.shields.io/badge/polkadot-E6007A?style=for-the-badge&logo=polkadot&logoColor=white
25
//!
26
//! # Sudo Pallet
27
//!
28
//! A pallet to provide a way to execute privileged runtime calls using a specified sudo ("superuser
29
//! do") account.
30
//!
31
//! ## Pallet API
32
//!
33
//! See the [`pallet`] module for more information about the interfaces this pallet exposes,
34
//! including its configuration trait, dispatchables, storage items, events and errors.
35
//!
36
//! ## Overview
37
//!
38
//! In Substrate blockchains, pallets may contain dispatchable calls that can only be called at
39
//! the system level of the chain (i.e. dispatchables that require a `Root` origin).
40
//! Setting a privileged account, called the _sudo key_, allows you to make such calls as an
41
//! extrinsic.
42
//!
43
//! Here's an example of a privileged function in another pallet:
44
//!
45
//! ```
46
//! #[frame_support::pallet]
47
//! pub mod pallet {
48
//! 	use super::*;
49
//! 	use frame_support::pallet_prelude::*;
50
//! 	use frame_system::pallet_prelude::*;
51
//!
52
//! 	#[pallet::pallet]
53
//! 	pub struct Pallet<T>(_);
54
//!
55
//! 	#[pallet::config]
56
//! 	pub trait Config: frame_system::Config {}
57
//!
58
//! 	#[pallet::call]
59
//! 	impl<T: Config> Pallet<T> {
60
//! 		#[pallet::weight(0)]
61
//!         pub fn privileged_function(origin: OriginFor<T>) -> DispatchResult {
62
//!             ensure_root(origin)?;
63
//!
64
//!             // do something...
65
//!
66
//!             Ok(())
67
//!         }
68
//! 	}
69
//! }
70
//! ```
71
//!
72
//! With the Sudo pallet configured in your chain's runtime you can execute this privileged
73
//! function by constructing a call using the [`sudo`](Pallet::sudo) dispatchable.
74
//!
75
//! To use this pallet in your runtime, a sudo key must be specified in the [`GenesisConfig`] of
76
//! the pallet. You can change this key at anytime once your chain is live using the
77
//! [`set_key`](Pallet::set_key) dispatchable, however <strong>only one sudo key can be set at a
78
//! time</strong>. The pallet also allows you to make a call using
79
//! [`sudo_unchecked_weight`](Pallet::sudo_unchecked_weight), which allows the sudo account to
80
//! execute a call with a custom weight.
81
//!
82
//! <div class="example-wrap" style="display:inline-block"><pre class="compile_fail"
83
//! style="white-space:normal;font:inherit;">
84
//! <strong>Note:</strong> this pallet is not meant to be used inside other pallets. It is only
85
//! meant to be used by constructing runtime calls from outside the runtime.
86
//! </pre></div>
87
//!
88
//! This pallet also defines a [`SignedExtension`](sp_runtime::traits::SignedExtension) called
89
//! [`CheckOnlySudoAccount`] to ensure that only signed transactions by the sudo account are
90
//! accepted by the transaction pool. The intended use of this signed extension is to prevent other
91
//! accounts from spamming the transaction pool for the initial phase of a chain, during which
92
//! developers may only want a sudo account to be able to make transactions.
93
//!
94
//! Learn more about the `Root` origin in the [`RawOrigin`](frame_system::RawOrigin) type
95
//! documentation.
96
//!
97
//! ### Examples
98
//!
99
//! 1. You can make a privileged runtime call using `sudo` with an account that matches the sudo
100
//!    key.
101
#![doc = docify::embed!("src/tests.rs", sudo_basics)]
102
//!
103
//! 2. Only an existing sudo key can set a new one.
104
#![doc = docify::embed!("src/tests.rs", set_key_basics)]
105
//!
106
//! 3. You can also make non-privileged calls using `sudo_as`.
107
#![doc = docify::embed!("src/tests.rs", sudo_as_emits_events_correctly)]
108
//!
109
//! ## Low Level / Implementation Details
110
//!
111
//! This pallet checks that the caller of its dispatchables is a signed account and ensures that the
112
//! caller matches the sudo key in storage.
113
//! A caller of this pallet's dispatchables does not pay any fees to dispatch a call. If the account
114
//! making one of these calls is not the sudo key, the pallet returns a [`Error::RequireSudo`]
115
//! error.
116
//!
117
//! Once an origin is verified, sudo calls use `dispatch_bypass_filter` from the
118
//! [`UnfilteredDispatchable`](frame_support::traits::UnfilteredDispatchable) trait to allow call
119
//! execution without enforcing any further origin checks.
120

            
121
#![deny(missing_docs)]
122
#![cfg_attr(not(feature = "std"), no_std)]
123

            
124
extern crate alloc;
125

            
126
use alloc::boxed::Box;
127

            
128
use sp_runtime::{traits::StaticLookup, DispatchResult};
129

            
130
use frame_support::{dispatch::GetDispatchInfo, traits::UnfilteredDispatchable};
131

            
132
mod extension;
133
#[cfg(test)]
134
mod mock;
135
#[cfg(test)]
136
mod tests;
137

            
138
#[cfg(feature = "runtime-benchmarks")]
139
mod benchmarking;
140
pub mod weights;
141
pub use weights::WeightInfo;
142

            
143
pub use extension::CheckOnlySudoAccount;
144
pub use pallet::*;
145

            
146
type AccountIdLookupOf<T> = <<T as frame_system::Config>::Lookup as StaticLookup>::Source;
147

            
148
13299
#[frame_support::pallet]
149
pub mod pallet {
150
	use super::{DispatchResult, *};
151
	use frame_support::pallet_prelude::*;
152
	use frame_system::{pallet_prelude::*, RawOrigin};
153

            
154
	/// Default preludes for [`Config`].
155
	pub mod config_preludes {
156
		use super::*;
157
		use frame_support::derive_impl;
158

            
159
		/// Default prelude sensible to be used in a testing environment.
160
		pub struct TestDefaultConfig;
161

            
162
		#[derive_impl(frame_system::config_preludes::TestDefaultConfig, no_aggregated_types)]
163
		impl frame_system::DefaultConfig for TestDefaultConfig {}
164

            
165
		#[frame_support::register_default_impl(TestDefaultConfig)]
166
		impl DefaultConfig for TestDefaultConfig {
167
			type WeightInfo = ();
168
			#[inject_runtime_type]
169
			type RuntimeEvent = ();
170
			#[inject_runtime_type]
171
			type RuntimeCall = ();
172
		}
173
	}
174
	#[pallet::config(with_default)]
175
	pub trait Config: frame_system::Config {
176
		/// The overarching event type.
177
		#[pallet::no_default_bounds]
178
		type RuntimeEvent: From<Event<Self>> + IsType<<Self as frame_system::Config>::RuntimeEvent>;
179

            
180
		/// A sudo-able call.
181
		#[pallet::no_default_bounds]
182
		type RuntimeCall: Parameter
183
			+ UnfilteredDispatchable<RuntimeOrigin = Self::RuntimeOrigin>
184
			+ GetDispatchInfo;
185

            
186
		/// Type representing the weight of this pallet
187
		type WeightInfo: WeightInfo;
188
	}
189

            
190
554790
	#[pallet::pallet]
191
	pub struct Pallet<T>(_);
192

            
193
75417
	#[pallet::call]
194
	impl<T: Config> Pallet<T> {
195
		/// Authenticates the sudo key and dispatches a function call with `Root` origin.
196
		#[pallet::call_index(0)]
197
		#[pallet::weight({
198
			let dispatch_info = call.get_dispatch_info();
199
			(
200
				T::WeightInfo::sudo().saturating_add(dispatch_info.weight),
201
				dispatch_info.class
202
			)
203
		})]
204
		pub fn sudo(
205
			origin: OriginFor<T>,
206
			call: Box<<T as Config>::RuntimeCall>,
207
9177
		) -> DispatchResultWithPostInfo {
208
9177
			Self::ensure_sudo(origin)?;
209

            
210
			let res = call.dispatch_bypass_filter(RawOrigin::Root.into());
211
			Self::deposit_event(Event::Sudid { sudo_result: res.map(|_| ()).map_err(|e| e.error) });
212

            
213
			// Sudo user does not pay a fee.
214
			Ok(Pays::No.into())
215
		}
216

            
217
		/// Authenticates the sudo key and dispatches a function call with `Root` origin.
218
		/// This function does not check the weight of the call, and instead allows the
219
		/// Sudo user to specify the weight of the call.
220
		///
221
		/// The dispatch origin for this call must be _Signed_.
222
		#[pallet::call_index(1)]
223
		#[pallet::weight((*weight, call.get_dispatch_info().class))]
224
		pub fn sudo_unchecked_weight(
225
			origin: OriginFor<T>,
226
			call: Box<<T as Config>::RuntimeCall>,
227
			weight: Weight,
228
513
		) -> DispatchResultWithPostInfo {
229
513
			Self::ensure_sudo(origin)?;
230
			let _ = weight; // We don't check the weight witness since it is a root call.
231

            
232
			let res = call.dispatch_bypass_filter(RawOrigin::Root.into());
233
			Self::deposit_event(Event::Sudid { sudo_result: res.map(|_| ()).map_err(|e| e.error) });
234

            
235
			// Sudo user does not pay a fee.
236
			Ok(Pays::No.into())
237
		}
238

            
239
		/// Authenticates the current sudo key and sets the given AccountId (`new`) as the new sudo
240
		/// key.
241
		#[pallet::call_index(2)]
242
		#[pallet::weight(T::WeightInfo::set_key())]
243
		pub fn set_key(
244
			origin: OriginFor<T>,
245
			new: AccountIdLookupOf<T>,
246
135
		) -> DispatchResultWithPostInfo {
247
135
			Self::ensure_sudo(origin)?;
248

            
249
			let new = T::Lookup::lookup(new)?;
250
			Self::deposit_event(Event::KeyChanged { old: Key::<T>::get(), new: new.clone() });
251
			Key::<T>::put(new);
252

            
253
			// Sudo user does not pay a fee.
254
			Ok(Pays::No.into())
255
		}
256

            
257
		/// Authenticates the sudo key and dispatches a function call with `Signed` origin from
258
		/// a given account.
259
		///
260
		/// The dispatch origin for this call must be _Signed_.
261
		#[pallet::call_index(3)]
262
		#[pallet::weight({
263
			let dispatch_info = call.get_dispatch_info();
264
			(
265
				T::WeightInfo::sudo_as().saturating_add(dispatch_info.weight),
266
				dispatch_info.class,
267
			)
268
		})]
269
		pub fn sudo_as(
270
			origin: OriginFor<T>,
271
			who: AccountIdLookupOf<T>,
272
			call: Box<<T as Config>::RuntimeCall>,
273
414
		) -> DispatchResultWithPostInfo {
274
414
			Self::ensure_sudo(origin)?;
275

            
276
			let who = T::Lookup::lookup(who)?;
277
			let res = call.dispatch_bypass_filter(RawOrigin::Signed(who).into());
278
			Self::deposit_event(Event::SudoAsDone {
279
				sudo_result: res.map(|_| ()).map_err(|e| e.error),
280
			});
281

            
282
			// Sudo user does not pay a fee.
283
			Ok(Pays::No.into())
284
		}
285

            
286
		/// Permanently removes the sudo key.
287
		///
288
		/// **This cannot be un-done.**
289
		#[pallet::call_index(4)]
290
		#[pallet::weight(T::WeightInfo::remove_key())]
291
2886
		pub fn remove_key(origin: OriginFor<T>) -> DispatchResultWithPostInfo {
292
2886
			Self::ensure_sudo(origin)?;
293

            
294
			Self::deposit_event(Event::KeyRemoved {});
295
			Key::<T>::kill();
296

            
297
			// Sudo user does not pay a fee.
298
			Ok(Pays::No.into())
299
		}
300
	}
301

            
302
	#[pallet::event]
303
	#[pallet::generate_deposit(pub(super) fn deposit_event)]
304
	pub enum Event<T: Config> {
305
		/// A sudo call just took place.
306
		Sudid {
307
			/// The result of the call made by the sudo user.
308
			sudo_result: DispatchResult,
309
		},
310
		/// The sudo key has been updated.
311
		KeyChanged {
312
			/// The old sudo key (if one was previously set).
313
			old: Option<T::AccountId>,
314
			/// The new sudo key (if one was set).
315
			new: T::AccountId,
316
		},
317
		/// The key was permanently removed.
318
		KeyRemoved,
319
		/// A [sudo_as](Pallet::sudo_as) call just took place.
320
		SudoAsDone {
321
			/// The result of the call made by the sudo user.
322
			sudo_result: DispatchResult,
323
		},
324
	}
325

            
326
26250
	#[pallet::error]
327
	/// Error for the Sudo pallet.
328
	pub enum Error<T> {
329
		/// Sender must be the Sudo account.
330
		RequireSudo,
331
	}
332

            
333
	/// The `AccountId` of the sudo key.
334
26256
	#[pallet::storage]
335
	pub(super) type Key<T: Config> = StorageValue<_, T::AccountId, OptionQuery>;
336

            
337
	#[pallet::genesis_config]
338
	#[derive(frame_support::DefaultNoBound)]
339
	pub struct GenesisConfig<T: Config> {
340
		/// The `AccountId` of the sudo key.
341
		pub key: Option<T::AccountId>,
342
	}
343

            
344
	#[pallet::genesis_build]
345
	impl<T: Config> BuildGenesisConfig for GenesisConfig<T> {
346
3
		fn build(&self) {
347
3
			Key::<T>::set(self.key.clone());
348
3
		}
349
	}
350

            
351
	impl<T: Config> Pallet<T> {
352
		/// Ensure that the caller is the sudo key.
353
13125
		pub(crate) fn ensure_sudo(origin: OriginFor<T>) -> DispatchResult {
354
13125
			let sender = ensure_signed_or_root(origin)?;
355

            
356
13125
			if let Some(sender) = sender {
357
13125
				if Key::<T>::get().map_or(false, |k| k == sender) {
358
					Ok(())
359
				} else {
360
13125
					Err(Error::<T>::RequireSudo.into())
361
				}
362
			} else {
363
				Ok(())
364
			}
365
13125
		}
366
	}
367
}