From 6ba635fcff5fc9471c690c9987bad352241e9930 Mon Sep 17 00:00:00 2001
From: Qinxuan Chen <koushiro.cqx@gmail.com>
Date: Fri, 4 Nov 2022 07:08:24 +0800
Subject: [PATCH] pallet-sudo: add `CheckOnlySudoAccount` signed extension
 (#12496)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* pallet-sudo: add `CheckSudoKey` signed extension

Signed-off-by: koushiro <koushiro.cqx@gmail.com>

* Rename CheckSudoKey => CheckOnlySudo

Signed-off-by: koushiro <koushiro.cqx@gmail.com>

* Rename extension name and Add some docs

* Apply review suggestions

* Update frame/sudo/src/extension.rs

Co-authored-by: Jegor Sidorenko <5252494+jsidorenko@users.noreply.github.com>

* Update frame/sudo/src/extension.rs

Signed-off-by: koushiro <koushiro.cqx@gmail.com>
Co-authored-by: Bastian Köcher <git@kchr.de>
Co-authored-by: Jegor Sidorenko <5252494+jsidorenko@users.noreply.github.com>
---
 substrate/frame/sudo/src/extension.rs | 107 ++++++++++++++++++++++++++
 substrate/frame/sudo/src/lib.rs       |  11 ++-
 2 files changed, 117 insertions(+), 1 deletion(-)
 create mode 100644 substrate/frame/sudo/src/extension.rs

diff --git a/substrate/frame/sudo/src/extension.rs b/substrate/frame/sudo/src/extension.rs
new file mode 100644
index 00000000000..068fa2ed928
--- /dev/null
+++ b/substrate/frame/sudo/src/extension.rs
@@ -0,0 +1,107 @@
+// This file is part of Substrate.
+
+// Copyright (C) 2022 Parity Technologies (UK) Ltd.
+// SPDX-License-Identifier: Apache-2.0
+
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// 	http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+use crate::{Config, Pallet};
+use codec::{Decode, Encode};
+use frame_support::{dispatch::DispatchInfo, ensure};
+use scale_info::TypeInfo;
+use sp_runtime::{
+	traits::{DispatchInfoOf, Dispatchable, SignedExtension},
+	transaction_validity::{
+		InvalidTransaction, TransactionPriority, TransactionValidity, TransactionValidityError,
+		UnknownTransaction, ValidTransaction,
+	},
+};
+use sp_std::{fmt, marker::PhantomData};
+
+/// Ensure that signed transactions are only valid if they are signed by sudo account.
+///
+/// In the initial phase of a chain without any tokens you can not prevent accounts from sending
+/// transactions.
+/// These transactions would enter the transaction pool as the succeed the validation, but would
+/// fail on applying them as they are not allowed/disabled/whatever. This would be some huge dos
+/// vector to any kind of chain. This extension solves the dos vector by preventing any kind of
+/// transaction entering the pool as long as it is not signed by the sudo account.
+#[derive(Clone, Eq, PartialEq, Encode, Decode, TypeInfo)]
+#[scale_info(skip_type_params(T))]
+pub struct CheckOnlySudoAccount<T: Config + Send + Sync>(PhantomData<T>);
+
+impl<T: Config + Send + Sync> Default for CheckOnlySudoAccount<T> {
+	fn default() -> Self {
+		Self(Default::default())
+	}
+}
+
+impl<T: Config + Send + Sync> fmt::Debug for CheckOnlySudoAccount<T> {
+	#[cfg(feature = "std")]
+	fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+		write!(f, "CheckOnlySudoAccount")
+	}
+
+	#[cfg(not(feature = "std"))]
+	fn fmt(&self, _: &mut fmt::Formatter) -> fmt::Result {
+		Ok(())
+	}
+}
+
+impl<T: Config + Send + Sync> CheckOnlySudoAccount<T> {
+	/// Creates new `SignedExtension` to check sudo key.
+	pub fn new() -> Self {
+		Self::default()
+	}
+}
+
+impl<T: Config + Send + Sync> SignedExtension for CheckOnlySudoAccount<T>
+where
+	<T as Config>::RuntimeCall: Dispatchable<Info = DispatchInfo>,
+{
+	const IDENTIFIER: &'static str = "CheckOnlySudoAccount";
+	type AccountId = T::AccountId;
+	type Call = <T as Config>::RuntimeCall;
+	type AdditionalSigned = ();
+	type Pre = ();
+
+	fn additional_signed(&self) -> Result<Self::AdditionalSigned, TransactionValidityError> {
+		Ok(())
+	}
+
+	fn validate(
+		&self,
+		who: &Self::AccountId,
+		_call: &Self::Call,
+		info: &DispatchInfoOf<Self::Call>,
+		_len: usize,
+	) -> TransactionValidity {
+		let sudo_key: T::AccountId = <Pallet<T>>::key().ok_or(UnknownTransaction::CannotLookup)?;
+		ensure!(*who == sudo_key, InvalidTransaction::BadSigner);
+
+		Ok(ValidTransaction {
+			priority: info.weight.ref_time() as TransactionPriority,
+			..Default::default()
+		})
+	}
+
+	fn pre_dispatch(
+		self,
+		who: &Self::AccountId,
+		call: &Self::Call,
+		info: &DispatchInfoOf<Self::Call>,
+		len: usize,
+	) -> Result<Self::Pre, TransactionValidityError> {
+		self.validate(who, call, info, len).map(|_| ())
+	}
+}
diff --git a/substrate/frame/sudo/src/lib.rs b/substrate/frame/sudo/src/lib.rs
index 75d15d23c68..c18ced89111 100644
--- a/substrate/frame/sudo/src/lib.rs
+++ b/substrate/frame/sudo/src/lib.rs
@@ -59,7 +59,7 @@
 //! 	use frame_system::pallet_prelude::*;
 //!
 //! 	#[pallet::pallet]
-//! 	pub struct Pallet<T>(_);
+//! 	pub struct Pallet<T>(PhantomData<T>);
 //!
 //! 	#[pallet::config]
 //! 	pub trait Config: frame_system::Config {}
@@ -79,6 +79,13 @@
 //! # fn main() {}
 //! ```
 //!
+//! ### Signed Extension
+//!
+//! The Sudo pallet defines the following extension:
+//!
+//!   - [`CheckOnlySudoAccount`]: Ensures that the signed transactions are only valid if they are
+//!     signed by sudo account.
+//!
 //! ## Genesis Config
 //!
 //! The Sudo pallet depends on the [`GenesisConfig`].
@@ -97,11 +104,13 @@ use sp_std::prelude::*;
 
 use frame_support::{dispatch::GetDispatchInfo, traits::UnfilteredDispatchable};
 
+mod extension;
 #[cfg(test)]
 mod mock;
 #[cfg(test)]
 mod tests;
 
+pub use extension::CheckOnlySudoAccount;
 pub use pallet::*;
 
 type AccountIdLookupOf<T> = <<T as frame_system::Config>::Lookup as StaticLookup>::Source;
-- 
GitLab