Skip to main content

pallet_domain_sudo/
lib.rs

1// Copyright (C) 2023 Subspace Labs, Inc.
2// SPDX-License-Identifier: Apache-2.0
3
4// Licensed under the Apache License, Version 2.0 (the "License");
5// you may not use this file except in compliance with the License.
6// You may obtain a copy of the License at
7//
8// 	http://www.apache.org/licenses/LICENSE-2.0
9//
10// Unless required by applicable law or agreed to in writing, software
11// distributed under the License is distributed on an "AS IS" BASIS,
12// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13// See the License for the specific language governing permissions and
14// limitations under the License.
15
16//! Pallet Domain sudo
17
18#![cfg_attr(not(feature = "std"), no_std)]
19// TODO: Remove once FRAME macros stop generating `.clone()` calls on `Copy` types with user spans
20#![expect(clippy::clone_on_copy, reason = "Comes from Substrate")]
21
22#[cfg(not(feature = "std"))]
23extern crate alloc;
24
25pub use pallet::*;
26
27#[frame_support::pallet]
28mod pallet {
29    #[cfg(not(feature = "std"))]
30    use alloc::boxed::Box;
31    use frame_support::dispatch::{GetDispatchInfo, RawOrigin};
32    use frame_support::pallet_prelude::*;
33    use frame_support::traits::UnfilteredDispatchable;
34    use frame_system::ensure_none;
35    use frame_system::pallet_prelude::OriginFor;
36    use sp_domain_sudo::{INHERENT_IDENTIFIER, InherentError, InherentType, IntoRuntimeCall};
37
38    #[pallet::config]
39    pub trait Config: frame_system::Config<RuntimeEvent: From<Event<Self>>> {
40        type RuntimeCall: Parameter
41            + UnfilteredDispatchable<RuntimeOrigin = Self::RuntimeOrigin>
42            + GetDispatchInfo;
43        type IntoRuntimeCall: IntoRuntimeCall<<Self as Config>::RuntimeCall>;
44    }
45
46    #[pallet::event]
47    #[pallet::generate_deposit(pub (super) fn deposit_event)]
48    pub enum Event<T: Config> {
49        /// A sudo call just took place.
50        Sudid {
51            /// The result of the call made by the sudo user.
52            sudo_result: DispatchResult,
53        },
54    }
55
56    /// Pallet Domain Sudo
57    #[pallet::pallet]
58    #[pallet::without_storage_info]
59    pub struct Pallet<T>(_);
60
61    #[pallet::call]
62    impl<T: Config> Pallet<T> {
63        /// Calls the underlying RuntimeCall with Root origin.
64        #[pallet::call_index(0)]
65        #[pallet::weight({
66            let dispatch_info = call.get_dispatch_info();
67            (dispatch_info.call_weight.saturating_add(dispatch_info.extension_weight), DispatchClass::Mandatory)
68        })]
69        pub fn sudo(origin: OriginFor<T>, call: Box<<T as Config>::RuntimeCall>) -> DispatchResult {
70            ensure_none(origin)?;
71
72            let res = call.dispatch_bypass_filter(RawOrigin::Root.into());
73            Self::deposit_event(Event::Sudid {
74                sudo_result: res.map(|_| ()).map_err(|e| e.error),
75            });
76
77            Ok(())
78        }
79    }
80
81    #[pallet::inherent]
82    impl<T: Config> ProvideInherent for Pallet<T> {
83        type Call = Call<T>;
84        type Error = InherentError;
85        const INHERENT_IDENTIFIER: InherentIdentifier = INHERENT_IDENTIFIER;
86
87        fn create_inherent(data: &InherentData) -> Option<Self::Call> {
88            let inherent_data = data
89                .get_data::<InherentType>(&INHERENT_IDENTIFIER)
90                .expect("Domain sudo inherent data not correctly encoded")
91                .expect("Domain sudo inherent data must be provided");
92
93            if let Some(encoded_call) = inherent_data.maybe_call {
94                let call = Box::new(T::IntoRuntimeCall::runtime_call(encoded_call));
95                Some(Call::sudo { call })
96            } else {
97                None
98            }
99        }
100
101        fn is_inherent_required(data: &InherentData) -> Result<Option<Self::Error>, Self::Error> {
102            let inherent_data = data
103                .get_data::<InherentType>(&INHERENT_IDENTIFIER)
104                .expect("Domain sudo inherent data not correctly encoded")
105                .expect("Domain sudo inherent data must be provided");
106
107            Ok(if inherent_data.maybe_call.is_none() {
108                None
109            } else {
110                Some(InherentError::MissingRuntimeCall)
111            })
112        }
113
114        fn check_inherent(call: &Self::Call, data: &InherentData) -> Result<(), Self::Error> {
115            let inherent_data = data
116                .get_data::<InherentType>(&INHERENT_IDENTIFIER)
117                .expect("Domain sudo inherent data not correctly encoded")
118                .expect("Domain sudo inherent data must be provided");
119
120            if let Some(encoded_call) = inherent_data.maybe_call {
121                let runtime_call = Box::new(T::IntoRuntimeCall::runtime_call(encoded_call));
122                if let Call::sudo { call } = call
123                    && call != &runtime_call
124                {
125                    return Err(InherentError::IncorrectRuntimeCall);
126                }
127            } else {
128                return Err(InherentError::MissingRuntimeCall);
129            }
130
131            Ok(())
132        }
133
134        fn is_inherent(call: &Self::Call) -> bool {
135            matches!(call, Call::sudo { .. })
136        }
137    }
138}